Merge "Move TextClassifier.getLocalTextClassifier() API."
diff --git a/.gitignore b/.gitignore
index 45884c4..d7aebc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 /.idea
 *.iml
+*.sw*
diff --git a/Android.bp b/Android.bp
index d5c0a34..6288940 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,7 +69,6 @@
         "core/java/android/app/ITaskStackListener.aidl",
         "core/java/android/app/IBackupAgent.aidl",
         "core/java/android/app/IEphemeralResolver.aidl",
-        "core/java/android/app/IInputForwarder.aidl",
         "core/java/android/app/IInstantAppResolver.aidl",
         "core/java/android/app/IInstrumentationWatcher.aidl",
         "core/java/android/app/INotificationManager.aidl",
@@ -209,6 +208,7 @@
         "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
         "core/java/android/hardware/usb/IUsbManager.aidl",
         "core/java/android/hardware/usb/IUsbSerialReader.aidl",
+        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/IConnectivityManager.aidl",
         "core/java/android/hardware/ISensorPrivacyListener.aidl",
         "core/java/android/hardware/ISensorPrivacyManager.aidl",
@@ -742,10 +742,8 @@
 
     static_libs: [
         "apex_aidl_interface-java",
-        "networkstack-aidl-interfaces-java",
         "framework-protos",
         "game-driver-protos",
-        "mediaplayer2-protos",
         "android.hidl.base-V1.0-java",
         "android.hardware.cas-V1.1-java",
         "android.hardware.cas-V1.0-java",
@@ -774,8 +772,8 @@
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
         "android.hardware.wifi-V1.0-java-constants",
-        "networkstack-aidl-interfaces-java",
-        "netd_aidl_interface-java",
+        "networkstack-aidl-framework-java",
+        "netd_aidl_parcelables-java",
         "devicepolicyprotosnano",
     ],
 
@@ -895,10 +893,8 @@
     srcs: [
         "core/java/android/net/ApfCapabilitiesParcelable.aidl",
         "core/java/android/net/DhcpResultsParcelable.aidl",
-        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/INetworkMonitor.aidl",
         "core/java/android/net/INetworkMonitorCallbacks.aidl",
-        "core/java/android/net/IIpMemoryStore.aidl",
         "core/java/android/net/INetworkStackConnector.aidl",
         "core/java/android/net/INetworkStackStatusCallback.aidl",
         "core/java/android/net/InitialConfigurationParcelable.aidl",
@@ -917,6 +913,16 @@
         "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
         "core/java/android/net/ip/IIpClient.aidl",
         "core/java/android/net/ip/IIpClientCallbacks.aidl",
+    ],
+    api_dir: "aidl/networkstack",
+}
+
+aidl_interface {
+    name: "networkstack-aidl-framework",
+    local_include_dir: "core/java",
+    srcs: [
+        "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl",
+        "core/java/android/net/IIpMemoryStore.aidl",
         "core/java/android/net/ipmemorystore/**/*.aidl",
     ],
     api_dir: "aidl/networkstack",
@@ -1189,12 +1195,21 @@
     "org/apache/http/params",
 ]
 
+// Make the api/current.txt file available for use by modules in other
+// directories.
+filegroup {
+    name: "frameworks-base-api-current.txt",
+    srcs: [
+        "api/current.txt",
+    ],
+}
+
 framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
      "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
      "-overview $(location core/java/overview.html) " +
      // Federate Support Library references against local API file.
      "-federate SupportLib https://developer.android.com " +
-     "-federationapi SupportLib $(location current/support-api.txt) "
+     "-federationapi SupportLib $(location :current-support-api) "
 
 framework_docs_only_libs = [
     "voip-common",
diff --git a/Android.mk b/Android.mk
index 9a91dd1..c58f7af 100644
--- a/Android.mk
+++ b/Android.mk
@@ -77,8 +77,6 @@
 
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
-# Check comment when you are updating the API
-update-api: doc-comment-check-docs
 
 # ==== hiddenapi lists =======================================
 ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
new file mode 100644
index 0000000..65c28fb
--- /dev/null
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "AutofillPerfTests",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/apct-tests/perftests/autofill/Android.mk b/apct-tests/perftests/autofill/Android.mk
deleted file mode 100644
index f4da40b..0000000
--- a/apct-tests/perftests/autofill/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    androidx.annotation_annotation \
-    apct-perftests-utils
-
-LOCAL_PACKAGE_NAME := AutofillPerfTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_COMPATIBILITY_SUITE += device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 9234849..0c30302 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -40,7 +40,7 @@
     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     private final KernelCpuThreadReader mKernelCpuThreadReader =
-            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000);
+            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000, 0);
 
     @Test
     public void timeReadCurrentProcessCpuUsage() {
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
new file mode 100644
index 0000000..508bf60
--- /dev/null
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "MultiUserPerfTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "apct-perftests-utils",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    certificate: "platform",
+}
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
deleted file mode 100644
index 5852044..0000000
--- a/apct-tests/perftests/multiuser/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    apct-perftests-utils
-
-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/utils/Android.bp b/apct-tests/perftests/utils/Android.bp
new file mode 100644
index 0000000..be85816
--- /dev/null
+++ b/apct-tests/perftests/utils/Android.bp
@@ -0,0 +1,9 @@
+java_library {
+    name: "apct-perftests-utils",
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+    ],
+    // Build all java files in the java subdirectory
+    srcs: ["**/*.java"],
+}
diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk
deleted file mode 100644
index 19f83c8..0000000
--- a/apct-tests/perftests/utils/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    androidx.annotation_annotation
-
-# Build all java files in the java subdirectory
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-# The name of the jar file to create
-LOCAL_MODULE := apct-perftests-utils
-
-# Build a static jar file.
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING
index 8a676e9..2cdf54b 100644
--- a/api/TEST_MAPPING
+++ b/api/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "CtsCurrentApiSignatureTestCases"
+    },
+    {
+      "name": "GtsUnofficialApisUsageTestCases"
     }
   ]
 }
diff --git a/api/current.txt b/api/current.txt
index d99cdd5a..2b560a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2222,6 +2222,7 @@
     field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
     field public static final int Theme = 16973829; // 0x1030005
     field public static final int ThemeOverlay = 16974407; // 0x1030247
+    field public static final int ThemeOverlay_DeviceDefault_Accent_DayNight = 16974564; // 0x10302e4
     field public static final int ThemeOverlay_Material = 16974408; // 0x1030248
     field public static final int ThemeOverlay_Material_ActionBar = 16974409; // 0x1030249
     field public static final int ThemeOverlay_Material_Dark = 16974411; // 0x103024b
@@ -2233,6 +2234,7 @@
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
     field public static final int Theme_DeviceDefault = 16974120; // 0x1030128
+    field public static final int Theme_DeviceDefault_DayNight = 16974563; // 0x10302e3
     field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e
     field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136
     field public static final int Theme_DeviceDefault_DialogWhenLarge_NoActionBar = 16974135; // 0x1030137
@@ -6684,7 +6686,7 @@
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate, @NonNull String);
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate[], @NonNull String, boolean);
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate[], @NonNull String, int);
-    method public void installSystemUpdate(@NonNull android.content.ComponentName, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.InstallUpdateCallback);
+    method public void installSystemUpdate(@NonNull android.content.ComponentName, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(@NonNull android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6951,8 +6953,8 @@
     field public static final int WIPE_SILENTLY = 8; // 0x8
   }
 
-  public abstract static class DevicePolicyManager.InstallUpdateCallback {
-    ctor public DevicePolicyManager.InstallUpdateCallback();
+  public abstract static class DevicePolicyManager.InstallSystemUpdateCallback {
+    ctor public DevicePolicyManager.InstallSystemUpdateCallback();
     method public void onInstallUpdateError(int, String);
     field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
     field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
@@ -9346,25 +9348,7 @@
     field public static final android.os.Parcelable.Creator<android.content.ComponentName> CREATOR;
   }
 
-  public interface ContentInterface {
-    method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
-    method public int bulkInsert(@NonNull android.net.Uri, @NonNull android.content.ContentValues[]) throws android.os.RemoteException;
-    method @Nullable public android.os.Bundle call(@NonNull String, @NonNull String, @Nullable String, @Nullable android.os.Bundle) throws android.os.RemoteException;
-    method @Nullable public android.net.Uri canonicalize(@NonNull android.net.Uri) throws android.os.RemoteException;
-    method public int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]) throws android.os.RemoteException;
-    method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String) throws android.os.RemoteException;
-    method @Nullable public String getType(@NonNull android.net.Uri) throws android.os.RemoteException;
-    method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues) throws android.os.RemoteException;
-    method @Nullable public android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
-    method @Nullable public android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
-    method @Nullable public android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
-    method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws android.os.RemoteException;
-    method public boolean refresh(@NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws android.os.RemoteException;
-    method @Nullable public android.net.Uri uncanonicalize(@NonNull android.net.Uri) throws android.os.RemoteException;
-    method public int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]) throws android.os.RemoteException;
-  }
-
-  public abstract class ContentProvider implements android.content.ComponentCallbacks2 android.content.ContentInterface {
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
     ctor public ContentProvider();
     method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
     method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
@@ -9417,7 +9401,7 @@
     method public void writeDataToPipe(@NonNull android.os.ParcelFileDescriptor, @NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable T);
   }
 
-  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public int bulkInsert(@NonNull android.net.Uri, @NonNull android.content.ContentValues[]) throws android.os.RemoteException;
@@ -9425,7 +9409,6 @@
     method @Nullable public android.os.Bundle call(@NonNull String, @NonNull String, @Nullable String, @Nullable android.os.Bundle) throws android.os.RemoteException;
     method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri) throws android.os.RemoteException;
     method public void close();
-    method public static void closeQuietly(android.content.ContentProviderClient);
     method public int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]) throws android.os.RemoteException;
     method @Nullable public android.content.ContentProvider getLocalContentProvider();
     method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String) throws android.os.RemoteException;
@@ -9500,8 +9483,8 @@
     method public void setKeepUpdated(boolean);
   }
 
-  public abstract class ContentResolver implements android.content.ContentInterface {
-    ctor public ContentResolver(android.content.Context);
+  public abstract class ContentResolver {
+    ctor public ContentResolver(@Nullable android.content.Context);
     method @Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull android.net.Uri);
     method @Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull String);
     method @Nullable public final android.content.ContentProviderClient acquireUnstableContentProviderClient(@NonNull android.net.Uri);
@@ -9517,7 +9500,6 @@
     method public static void cancelSync(android.content.SyncRequest);
     method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri);
     method public final int delete(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable String, @Nullable String[]);
-    method public android.os.Bundle getCache(android.net.Uri);
     method @Deprecated public static android.content.SyncInfo getCurrentSync();
     method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
     method public static int getIsSyncable(android.accounts.Account, String);
@@ -9548,7 +9530,6 @@
     method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle) throws java.io.FileNotFoundException;
     method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
-    method public void putCache(android.net.Uri, android.os.Bundle);
     method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String);
     method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal);
     method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
@@ -9568,6 +9549,8 @@
     method public final void unregisterContentObserver(@NonNull android.database.ContentObserver);
     method public final int update(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]);
     method public static void validateSyncExtrasBundle(android.os.Bundle);
+    method public static android.content.ContentResolver wrap(@NonNull android.content.ContentProvider);
+    method public static android.content.ContentResolver wrap(@NonNull android.content.ContentProviderClient);
     field public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
     field public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
@@ -9609,10 +9592,10 @@
 
   public class ContentUris {
     ctor public ContentUris();
-    method public static android.net.Uri.Builder appendId(android.net.Uri.Builder, long);
-    method public static long parseId(android.net.Uri);
-    method public static android.net.Uri removeId(android.net.Uri);
-    method public static android.net.Uri withAppendedId(android.net.Uri, long);
+    method @NonNull public static android.net.Uri.Builder appendId(@NonNull android.net.Uri.Builder, long);
+    method public static long parseId(@NonNull android.net.Uri);
+    method @NonNull public static android.net.Uri removeId(@NonNull android.net.Uri);
+    method @NonNull public static android.net.Uri withAppendedId(@NonNull android.net.Uri, long);
   }
 
   public final class ContentValues implements android.os.Parcelable {
@@ -10239,8 +10222,7 @@
     field public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS";
     field public static final String ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED";
     field public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED";
-    field public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
-    field public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
+    field @Deprecated public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
     field public static final String ACTION_MEDIA_SCANNER_STARTED = "android.intent.action.MEDIA_SCANNER_STARTED";
     field public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED";
     field public static final String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE";
@@ -10302,6 +10284,7 @@
     field public static final String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
     field public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
     field public static final String ACTION_VIEW = "android.intent.action.VIEW";
+    field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
     field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
     field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
     field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -10386,6 +10369,7 @@
     field public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
     field public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
     field public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY";
+    field public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
     field public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
     field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
@@ -10638,6 +10622,14 @@
     method @Deprecated public void onLoadComplete(android.content.Loader<D>, D);
   }
 
+  public final class LocusId implements android.os.Parcelable {
+    ctor public LocusId(@NonNull android.net.Uri);
+    method public int describeContents();
+    method @NonNull public android.net.Uri getUri();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.LocusId> CREATOR;
+  }
+
   public class MutableContextWrapper extends android.content.ContextWrapper {
     ctor public MutableContextWrapper(android.content.Context);
     method public void setBaseContext(android.content.Context);
@@ -13066,6 +13058,10 @@
     method public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String, String);
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
+    method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
+    method public boolean getDistinct();
+    method public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
+    method public boolean getStrict();
     method public String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
@@ -14098,6 +14094,34 @@
     ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
   }
 
+  public class HardwareRenderer {
+    ctor public HardwareRenderer();
+    method public void clearContent();
+    method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
+    method public void destroy();
+    method public boolean isOpaque();
+    method public void notifyFramePending();
+    method public void setContentRoot(@Nullable android.graphics.RenderNode);
+    method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+    method public void setLightSourceGeometry(float, float, float, float);
+    method public void setName(String);
+    method public void setOpaque(boolean);
+    method public void setStopped(boolean);
+    method public void setSurface(@Nullable android.view.Surface);
+    field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4
+    field public static final int SYNC_FRAME_DROPPED = 8; // 0x8
+    field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2
+    field public static final int SYNC_OK = 0; // 0x0
+    field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1
+  }
+
+  public final class HardwareRenderer.FrameRenderRequest {
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long);
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean);
+    method public int syncAndDraw();
+  }
+
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public void close();
     method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int);
@@ -22676,6 +22700,7 @@
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
     field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
     field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+    field public static final int STATE_2ND_CODE_LOCK = 65536; // 0x10000
     field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
     field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
     field public static final int STATE_BIT_SYNC = 2; // 0x2
@@ -23381,7 +23406,7 @@
     method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
@@ -23416,6 +23441,8 @@
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -25973,6 +26000,7 @@
     method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
     method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object);
     method public void close();
+    method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
     method @NonNull public String getSessionId();
     method @NonNull public android.media.Session2Token getSessionToken();
     method public boolean isPlaybackActive();
@@ -25999,6 +26027,7 @@
     method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
     method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
     method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
+    method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
     method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
   }
 
@@ -26065,6 +26094,15 @@
     field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
   }
 
+  public interface MicrophoneDirection {
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
+    field public static final int MIC_DIRECTION_BACK = 2; // 0x2
+    field public static final int MIC_DIRECTION_EXTERNAL = 3; // 0x3
+    field public static final int MIC_DIRECTION_FRONT = 1; // 0x1
+    field public static final int MIC_DIRECTION_UNSPECIFIED = 0; // 0x0
+  }
+
   public final class MicrophoneInfo {
     method @NonNull public String getAddress();
     method public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getChannelMapping();
@@ -27446,6 +27484,7 @@
     method public void seekTo(long);
     method public void sendCustomAction(@NonNull android.media.session.PlaybackState.CustomAction, @Nullable android.os.Bundle);
     method public void sendCustomAction(@NonNull String, @Nullable android.os.Bundle);
+    method public void setPlaybackSpeed(float);
     method public void setRating(android.media.Rating);
     method public void skipToNext();
     method public void skipToPrevious();
@@ -27496,6 +27535,7 @@
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
     method public void onRewind();
     method public void onSeekTo(long);
+    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(@NonNull android.media.Rating);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
@@ -29908,7 +29948,7 @@
     method public java.util.List<android.net.wifi.ScanResult> getScanResults();
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
-    method public boolean isDeviceToApRttSupported();
+    method @Deprecated public boolean isDeviceToApRttSupported();
     method public boolean isEasyConnectSupported();
     method public boolean isEnhancedPowerReportingSupported();
     method public boolean isOweSupported();
@@ -34576,11 +34616,9 @@
     ctor public FileUriExposedException(String);
   }
 
-  public class FileUtils {
+  public final class FileUtils {
     method public static void closeQuietly(@Nullable AutoCloseable);
     method public static void closeQuietly(@Nullable java.io.FileDescriptor);
-    method public static long copy(@NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
-    method public static long copy(@NonNull java.io.File, @NonNull java.io.File, @Nullable android.os.CancellationSignal, @Nullable java.util.concurrent.Executor, @Nullable android.os.FileUtils.ProgressListener) throws java.io.IOException;
     method public static long copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws java.io.IOException;
     method public static long copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream, @Nullable android.os.CancellationSignal, @Nullable java.util.concurrent.Executor, @Nullable android.os.FileUtils.ProgressListener) throws java.io.IOException;
     method public static long copy(@NonNull java.io.FileDescriptor, @NonNull java.io.FileDescriptor) throws java.io.IOException;
@@ -35051,7 +35089,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, String);
     method public void reboot(String);
     method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor);
-    method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback);
+    method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -35652,6 +35690,7 @@
     method public String getMountedObbPath(String);
     method @NonNull public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method @Nullable public android.os.storage.StorageVolume getStorageVolume(java.io.File);
+    method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri);
     method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException;
     method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
@@ -38124,38 +38163,26 @@
     method public static android.net.Uri buildRootsUri(String);
     method public static android.net.Uri buildSearchDocumentsUri(String, String, String);
     method public static android.net.Uri buildTreeDocumentUri(String, String);
-    method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, String, String) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, String, String) throws java.io.FileNotFoundException;
-    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
-    method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static void ejectRoot(android.content.ContentInterface, android.net.Uri);
-    method @Deprecated public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
-    method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+    method @Nullable public static android.net.Uri copyDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
+    method @Nullable public static android.net.Uri createDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Nullable public static android.content.IntentSender createWebLinkIntent(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @Nullable android.os.Bundle) throws java.io.FileNotFoundException;
+    method public static boolean deleteDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
+    method public static void ejectRoot(@NonNull android.content.ContentResolver, @NonNull android.net.Uri);
+    method @Nullable public static android.provider.DocumentsContract.Path findDocumentPath(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
     method public static String getDocumentId(android.net.Uri);
-    method @Nullable public static android.os.Bundle getDocumentMetadata(@NonNull android.content.ContentInterface, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method @Nullable public static android.os.Bundle getDocumentMetadata(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
+    method @Nullable public static android.graphics.Bitmap getDocumentThumbnail(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.graphics.Point, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public static String getRootId(android.net.Uri);
     method public static String getSearchDocumentsQuery(android.net.Uri);
     method public static String getTreeDocumentId(android.net.Uri);
-    method public static boolean isChildDocument(@NonNull android.content.ContentInterface, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static boolean isChildDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
     method public static boolean isDocumentUri(android.content.Context, @Nullable android.net.Uri);
     method public static boolean isRootUri(@NonNull android.content.Context, @Nullable android.net.Uri);
     method public static boolean isRootsUri(@NonNull android.content.Context, @Nullable android.net.Uri);
     method public static boolean isTreeUri(android.net.Uri);
-    method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method @Deprecated public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, String) throws java.io.FileNotFoundException;
-    method @Deprecated public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, String) throws java.io.FileNotFoundException;
+    method @Nullable public static android.net.Uri moveDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
+    method public static boolean removeDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
+    method @Nullable public static android.net.Uri renameDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException;
     field public static final String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
     field public static final String EXTRA_ERROR = "error";
     field public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -38590,13 +38617,13 @@
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
     ctor public MediaStore.Images.Media();
-    method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
+    method @Deprecated public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
     method public static android.net.Uri getContentUri(String);
-    method public static String insertImage(android.content.ContentResolver, String, String, String) throws java.io.FileNotFoundException;
-    method public static String insertImage(android.content.ContentResolver, android.graphics.Bitmap, String, String);
-    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
-    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String);
-    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String[], String);
+    method @Deprecated public static String insertImage(android.content.ContentResolver, String, String, String) throws java.io.FileNotFoundException;
+    method @Deprecated public static String insertImage(android.content.ContentResolver, android.graphics.Bitmap, String, String);
+    method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
+    method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String);
+    method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String[], String);
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
     field public static final String DEFAULT_SORT_ORDER = "bucket_display_name";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
@@ -38665,7 +38692,7 @@
 
   public static final class MediaStore.Video {
     ctor public MediaStore.Video();
-    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
+    method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
     field public static final String DEFAULT_SORT_ORDER = "_display_name";
   }
 
@@ -41548,17 +41575,18 @@
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
-    ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int);
+    ctor public Adjustment(String, String, android.os.Bundle, CharSequence, android.os.UserHandle);
     method public int describeContents();
     method public CharSequence getExplanation();
     method public String getKey();
     method public String getPackage();
     method public android.os.Bundle getSignals();
-    method public int getUser();
+    method public android.os.UserHandle getUserHandle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -41616,12 +41644,14 @@
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(@NonNull String);
-    method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+    method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
+    method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
+    method public final void unsnoozeNotification(String);
     field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
     field public static final int SOURCE_FROM_APP = 0; // 0x0
     field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
@@ -41954,7 +41984,6 @@
 
   public class VoiceInteractionService extends android.app.Service {
     ctor public VoiceInteractionService();
-    method public final void clearTranscription(boolean);
     method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
     method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
@@ -41964,8 +41993,7 @@
     method public void onReady();
     method public void onShutdown();
     method public void setDisabledShowContext(int);
-    method public final void setTranscription(@NonNull String);
-    method public final void setVoiceState(int);
+    method public final void setUiHints(@NonNull android.os.Bundle);
     method public void showSession(android.os.Bundle, int);
     field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final String SERVICE_META_DATA = "android.voice_interaction";
@@ -42116,7 +42144,7 @@
     method protected void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
-    method public android.content.Context getDisplayContext();
+    method @Nullable public android.content.Context getDisplayContext();
     method public android.view.SurfaceHolder getSurfaceHolder();
     method public boolean isPreview();
     method public boolean isVisible();
@@ -44382,6 +44410,7 @@
     method public int getChannelNumber();
     method public String getMccString();
     method public String getMncString();
+    method public long getNci();
     method public int getPci();
     method public int getTac();
     method public void writeToParcel(android.os.Parcel, int);
@@ -44395,6 +44424,7 @@
     method public String getMccString();
     method public String getMncString();
     method @Nullable public String getMobileNetworkOperator();
+    method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
   }
@@ -45069,12 +45099,12 @@
     method public boolean canChangeDtmfToneLength();
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
     method public int getCarrierIdFromSimMccMnc();
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
     method public int getDataActivity();
@@ -45104,7 +45134,7 @@
     method public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -45146,8 +45176,8 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
     method public String sendEnvelopeWithStatus(String);
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -45216,7 +45246,6 @@
     field public static final String EXTRA_STATE_RINGING;
     field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
     field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
-    field public static final int INVALID_CARD_ID = -1; // 0xffffffff
     field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
@@ -45255,7 +45284,9 @@
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
     field public static final int SIM_STATE_UNKNOWN = 0; // 0x0
+    field public static final int UNINITIALIZED_CARD_ID = -2; // 0xfffffffe
     field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
+    field public static final int UNSUPPORTED_CARD_ID = -1; // 0xffffffff
     field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
     field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
     field public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -48997,6 +49028,7 @@
   }
 
   public final class StatsLog {
+    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public static boolean logBinaryPushStateChanged(@NonNull String, long, int, int, @NonNull long[]);
     method public static boolean logEvent(int);
     method public static boolean logStart(int);
     method public static boolean logStop(int);
@@ -49216,6 +49248,7 @@
     ctor public ContextThemeWrapper(android.content.Context, android.content.res.Resources.Theme);
     method public void applyOverrideConfiguration(android.content.res.Configuration);
     method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean);
+    method public void setTheme(@Nullable android.content.res.Resources.Theme);
   }
 
   public final class Display {
@@ -50804,7 +50837,7 @@
     method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
-    method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack();
+    method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(@AttrRes int);
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Integer> getAttributeSourceResourceMap();
     method @android.view.ViewDebug.ExportedProperty @Nullable public String[] getAutofillHints();
     method public final android.view.autofill.AutofillId getAutofillId();
@@ -50854,6 +50887,8 @@
     method public void getHitRect(android.graphics.Rect);
     method public int getHorizontalFadingEdgeLength();
     method protected int getHorizontalScrollbarHeight();
+    method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarThumbDrawable();
+    method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarTrackDrawable();
     method @android.view.ViewDebug.CapturedViewProperty @IdRes public int getId();
     method @android.view.ViewDebug.ExportedProperty(category="accessibility", mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to="noHideDescendants")}) public int getImportantForAccessibility();
     method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForAutofill();
@@ -50944,6 +50979,8 @@
     method public long getUniqueDrawingId();
     method public int getVerticalFadingEdgeLength();
     method public int getVerticalScrollbarPosition();
+    method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
+    method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
     method public int getVerticalScrollbarWidth();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.VISIBLE, to="VISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.INVISIBLE, to="INVISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.GONE, to="GONE")}) public int getVisibility();
@@ -51187,6 +51224,8 @@
     method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
     method public void setHorizontalScrollBarEnabled(boolean);
+    method public void setHorizontalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setHorizontalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
     method public void setHovered(boolean);
     method public void setId(@IdRes int);
     method public void setImportantForAccessibility(int);
@@ -51276,6 +51315,8 @@
     method public void setVerticalFadingEdgeEnabled(boolean);
     method public void setVerticalScrollBarEnabled(boolean);
     method public void setVerticalScrollbarPosition(int);
+    method public void setVerticalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setVerticalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
     method public void setVisibility(int);
     method @Deprecated public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
@@ -51734,6 +51775,7 @@
     method public android.view.View getChildAt(int);
     method public int getChildCount();
     method protected int getChildDrawingOrder(int, int);
+    method public final int getChildDrawingOrder(int);
     method public static int getChildMeasureSpec(int, int, int);
     method protected boolean getChildStaticTransformation(android.view.View, android.view.animation.Transformation);
     method public boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -53439,16 +53481,17 @@
 
   public final class ContentCaptureContext implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.contentcapture.ContentCaptureContext forLocusId(@NonNull android.net.Uri);
+    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.content.LocusId getLocusId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureContext> CREATOR;
   }
 
   public static final class ContentCaptureContext.Builder {
-    ctor public ContentCaptureContext.Builder();
+    ctor public ContentCaptureContext.Builder(@NonNull android.content.LocusId);
     method public android.view.contentcapture.ContentCaptureContext build();
-    method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setAction(@NonNull String);
     method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setExtras(@NonNull android.os.Bundle);
-    method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setUri(@NonNull android.net.Uri);
   }
 
   public final class ContentCaptureManager {
@@ -53462,6 +53505,7 @@
     method public void close();
     method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
     method public final void destroy();
+    method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
     method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
     method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
@@ -53469,6 +53513,7 @@
     method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
     method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
     method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
+    method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
   }
 
   public final class ContentCaptureSessionId implements android.os.Parcelable {
@@ -53479,8 +53524,8 @@
 
   public final class UserDataRemovalRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public java.util.List<android.view.contentcapture.UserDataRemovalRequest.LocusIdRequest> getLocusIdRequests();
     method @NonNull public String getPackageName();
-    method @NonNull public java.util.List<android.view.contentcapture.UserDataRemovalRequest.UriRequest> getUriRequests();
     method public boolean isForEverything();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.UserDataRemovalRequest> CREATOR;
@@ -53488,13 +53533,13 @@
 
   public static final class UserDataRemovalRequest.Builder {
     ctor public UserDataRemovalRequest.Builder();
-    method public android.view.contentcapture.UserDataRemovalRequest.Builder addUri(@NonNull android.net.Uri, boolean);
+    method public android.view.contentcapture.UserDataRemovalRequest.Builder addLocusId(@NonNull android.content.LocusId, boolean);
     method @NonNull public android.view.contentcapture.UserDataRemovalRequest build();
     method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder forEverything();
   }
 
-  public final class UserDataRemovalRequest.UriRequest {
-    method @NonNull public android.net.Uri getUri();
+  public final class UserDataRemovalRequest.LocusIdRequest {
+    method @NonNull public android.content.LocusId getLocusId();
     method @NonNull public boolean isRecursive();
   }
 
@@ -53986,6 +54031,10 @@
     ctor public PropertyReader.PropertyTypeMismatchException(int, @NonNull String, @NonNull String);
   }
 
+  public final class WindowInspector {
+    method @NonNull public static java.util.List<android.view.View> getGlobalWindowViews();
+  }
+
 }
 
 package android.view.textclassifier {
@@ -55728,6 +55777,7 @@
     method public int getDropDownVerticalOffset();
     method public int getDropDownWidth();
     method protected android.widget.Filter getFilter();
+    method public int getInputMethodMode();
     method @Deprecated public android.widget.AdapterView.OnItemClickListener getItemClickListener();
     method @Deprecated public android.widget.AdapterView.OnItemSelectedListener getItemSelectedListener();
     method public int getListSelection();
@@ -55752,6 +55802,7 @@
     method public void setDropDownHorizontalOffset(int);
     method public void setDropDownVerticalOffset(int);
     method public void setDropDownWidth(int);
+    method public void setInputMethodMode(int);
     method public void setListSelection(int);
     method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener);
     method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
diff --git a/api/removed.txt b/api/removed.txt
index f5bd434..c4ed871 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -558,7 +558,7 @@
 
   public class TelephonyManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 50f5d280..ea03472 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -285,9 +285,11 @@
     method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
     method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
+    method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
     method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+    method public void setDeviceLocales(@NonNull android.os.LocaleList);
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
   }
@@ -302,16 +304,18 @@
   }
 
   public class AppOpsManager {
-    method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+    method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static String[] getOpStrs();
     method @Deprecated @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, String, int[]);
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
+    method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int);
     method public static int opToDefaultMode(@NonNull String);
     method @Nullable public static String opToPermission(@NonNull String);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
     field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
+    field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -390,6 +394,17 @@
     field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
   }
 
+  public static final class AppOpsManager.HistoricalOpsRequest {
+  }
+
+  public static final class AppOpsManager.HistoricalOpsRequest.Builder {
+    ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int);
+  }
+
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
@@ -439,6 +454,7 @@
 
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
+    method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setAllowBackgroundActivityStarts(boolean);
     method public void setDontSendToRestrictedApps(boolean);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long);
     method public android.os.Bundle toBundle();
@@ -545,15 +561,17 @@
   }
 
   public class StatusBarManager {
-    method public android.util.Pair<java.lang.Integer,java.lang.Integer> getDisableFlags();
+    method public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method public void setDisabledForSetup(boolean);
-    field public static final int DISABLE2_NONE = 0; // 0x0
-    field public static final int DISABLE_EXPAND = 65536; // 0x10000
-    field public static final int DISABLE_HOME = 2097152; // 0x200000
-    field public static final int DISABLE_NONE = 0; // 0x0
-    field public static final int DISABLE_NOTIFICATION_ALERTS = 262144; // 0x40000
-    field public static final int DISABLE_RECENT = 16777216; // 0x1000000
-    field public static final int DISABLE_SEARCH = 33554432; // 0x2000000
+  }
+
+  public static final class StatusBarManager.DisableInfo {
+    method public boolean areNoComponentsDisabled();
+    method public boolean isNavigateToHomeDisabled();
+    method public boolean isNotificationPeekingDisabled();
+    method public boolean isRecentsDisabled();
+    method public boolean isSearchDisabled();
+    method public boolean isStatusBarExpansionDisabled();
   }
 
   public final class Vr2dDisplayProperties implements android.os.Parcelable {
@@ -641,7 +659,7 @@
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
     method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
-    method public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+    method @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
@@ -1278,12 +1296,14 @@
 
 package android.content {
 
-  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
   }
 
-  public abstract class ContentResolver implements android.content.ContentInterface {
+  public abstract class ContentResolver {
+    method @Nullable public android.os.Bundle getCache(@NonNull android.net.Uri);
     method public android.graphics.drawable.Drawable getTypeDrawable(String);
+    method public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle);
   }
 
   public abstract class Context {
@@ -1364,6 +1384,7 @@
     field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
     field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+    field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
     field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
     field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
     field public static final String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
@@ -1374,6 +1395,7 @@
     field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
+    field public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
     field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
     field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
@@ -1578,7 +1600,7 @@
     method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method public void sendDeviceCustomizationReadyBroadcast();
     method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], @android.content.pm.PackageManager.DistractionRestriction int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int);
     method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
     method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
@@ -1656,9 +1678,6 @@
     method public abstract void onDexModuleRegistered(String, boolean, String);
   }
 
-  @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.content.pm.PackageManager.RESTRICTION_NONE, android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.DistractionRestriction {
-  }
-
   public static interface PackageManager.OnPermissionsChangedListener {
     method public void onPermissionsChanged(int);
   }
@@ -3403,13 +3422,13 @@
 
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public android.media.AudioAttributes getAttributes();
-    method public String getClientId();
+    method @NonNull public android.media.AudioAttributes getAttributes();
+    method @NonNull public String getClientId();
     method public int getClientUid();
     method public int getFlags();
     method public int getGainRequest();
     method public int getLossReceived();
-    method public String getPackageName();
+    method @NonNull public String getPackageName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR;
   }
@@ -3469,7 +3488,7 @@
     field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -3478,6 +3497,18 @@
     method public android.media.AudioRecord.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
   }
 
+  public class HwAudioSource {
+    method public void start();
+    method public void stop();
+  }
+
+  public static class HwAudioSource.Builder {
+    ctor public HwAudioSource.Builder();
+    method @NonNull public android.media.HwAudioSource build();
+    method @NonNull public android.media.HwAudioSource.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo);
+  }
+
   public final class MediaRecorder.AudioSource {
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
@@ -3598,6 +3629,28 @@
     method public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException;
   }
 
+  public final class AudioProductStrategies implements java.lang.Iterable<android.media.audiopolicy.AudioProductStrategy> android.os.Parcelable {
+    ctor public AudioProductStrategies();
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributesForLegacyStreamType(int);
+    method @NonNull public android.media.AudioAttributes getAudioAttributesForProductStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @Nullable public android.media.audiopolicy.AudioProductStrategy getById(int);
+    method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+    method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
+    method public int size();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategies> CREATOR;
+  }
+
+  public final class AudioProductStrategy implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method public int getId();
+    method @NonNull public String name();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR;
+  }
+
 }
 
 package android.media.session {
@@ -3927,7 +3980,7 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -4624,7 +4677,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
-    method public boolean isDeviceToDeviceRttSupported();
+    method @Deprecated public boolean isDeviceToDeviceRttSupported();
     method public boolean isPortableHotspotSupported();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
@@ -5303,6 +5356,10 @@
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
 
+  public final class LocaleList implements android.os.Parcelable {
+    method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale);
+  }
+
   public final class NativeHandle implements java.io.Closeable {
     ctor public NativeHandle();
     ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean);
@@ -5317,6 +5374,7 @@
 
   public final class PowerManager {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveMode();
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
@@ -5780,6 +5838,13 @@
     field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
   }
 
+  public static interface DeviceConfig.Rollback {
+    field public static final String BOOT_NAMESPACE = "rollback_boot";
+    field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
+    field public static final String NAMESPACE = "rollback";
+    field public static final String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
+  }
+
   public static interface DeviceConfig.Runtime {
     field public static final String NAMESPACE = "runtime";
     field public static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
@@ -5939,6 +6004,7 @@
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+    field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
   }
 
@@ -6257,6 +6323,8 @@
     ctor public AugmentedAutofillService();
     method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+    method public void onConnected();
+    method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
@@ -6294,20 +6362,11 @@
   }
 
   public abstract class PresentationParams {
-    method public int getFlags();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
     method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
-    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
-    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
-    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
-    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
-    field public static final int FLAG_HOST_IME = 16; // 0x10
-    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
   }
 
   public abstract static class PresentationParams.Area {
     method @NonNull public android.graphics.Rect getBounds();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
   }
 
 }
@@ -6333,6 +6392,7 @@
 
   public abstract class ContentCaptureService extends android.app.Service {
     ctor public ContentCaptureService();
+    method public final void disableContentCaptureServices();
     method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData);
     method public void onConnected();
     method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
@@ -6341,7 +6401,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -6512,6 +6573,17 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int);
+    ctor protected Adjustment(android.os.Parcel);
+    method public int getUser();
+    field public static final String KEY_PEOPLE = "key_people";
+  }
+
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor protected NotificationStats(android.os.Parcel);
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(String, CharSequence, CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
@@ -6968,6 +7040,7 @@
     field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+    field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
     field public static final int TTY_MODE_FULL = 1; // 0x1
     field public static final int TTY_MODE_HCO = 2; // 0x2
     field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -7854,7 +7927,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
-    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -8242,6 +8315,7 @@
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
     method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+    method public boolean hasKnownUserIntentEmergency();
     method public boolean isEmergencyCallTesting();
     method public boolean isVideoCall();
     method public boolean isVideoPaused();
@@ -8254,6 +8328,7 @@
     method public void setEmergencyCallTesting(boolean);
     method public void setEmergencyServiceCategories(int);
     method public void setEmergencyUrns(java.util.List<java.lang.String>);
+    method public void setHasKnownUserIntentEmergency(boolean);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -9296,7 +9371,8 @@
 package android.view.autofill {
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
   }
 
 }
@@ -9304,20 +9380,18 @@
 package android.view.contentcapture {
 
   public final class ContentCaptureContext implements android.os.Parcelable {
-    method @Nullable public String getAction();
     method @Nullable public android.content.ComponentName getActivityComponent();
     method public int getDisplayId();
-    method @Nullable public android.os.Bundle getExtras();
     method public int getFlags();
     method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
     method public int getTaskId();
-    method @Nullable public android.net.Uri getUri();
     field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
     field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
   }
 
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -9326,6 +9400,7 @@
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
@@ -9335,7 +9410,6 @@
 
   public final class ContentCaptureManager {
     method public boolean isContentCaptureFeatureEnabled();
-    method public void setContentCaptureFeatureEnabled(boolean);
   }
 
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1a7e4cb..963584c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -101,8 +101,8 @@
   public class AppOpsManager {
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
-    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
-    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(int, @Nullable String, @Nullable String[], long, long, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static int getNumOps();
     method public static String[] getOpStrs();
     method public boolean isOperationActive(int, int, String);
@@ -206,6 +206,17 @@
     field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
   }
 
+  public static final class AppOpsManager.HistoricalOpsRequest {
+  }
+
+  public static final class AppOpsManager.HistoricalOpsRequest.Builder {
+    ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int);
+  }
+
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
@@ -472,11 +483,11 @@
 
 package android.content {
 
-  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
   }
 
-  public abstract class ContentResolver implements android.content.ContentInterface {
+  public abstract class ContentResolver {
     method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int);
   }
 
@@ -516,13 +527,20 @@
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method @Nullable public abstract String[] getNamesForUids(int[]);
     method public abstract String getPermissionControllerPackageName();
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
     method public String getWellbeingPackageName();
     method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(String, String, int, int, @NonNull android.os.UserHandle);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+    field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
+    field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
+    field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
+    field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -802,6 +820,7 @@
 
   public class LocationManager {
     method public String[] getBackgroundThrottlingWhitelist();
+    method public String[] getIgnoreSettingsWhitelist();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, android.os.UserHandle);
@@ -936,7 +955,7 @@
   }
 
   public class ConnectivityManager {
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
   }
@@ -996,6 +1015,7 @@
     method public int[] getCapabilities();
     method public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
+    field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
   public class NetworkStack {
@@ -1281,7 +1301,7 @@
     method public static java.io.File getStorageDirectory();
   }
 
-  public class FileUtils {
+  public final class FileUtils {
     method public static boolean contains(java.io.File, java.io.File);
   }
 
@@ -1815,6 +1835,7 @@
 
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+    field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
 
@@ -2036,6 +2057,8 @@
     ctor public AugmentedAutofillService();
     method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+    method public void onConnected();
+    method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
@@ -2073,20 +2096,11 @@
   }
 
   public abstract class PresentationParams {
-    method public int getFlags();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
     method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
-    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
-    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
-    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
-    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
-    field public static final int FLAG_HOST_IME = 16; // 0x10
-    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
   }
 
   public abstract static class PresentationParams.Area {
     method @NonNull public android.graphics.Rect getBounds();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
   }
 
 }
@@ -2102,6 +2116,7 @@
 
   public abstract class ContentCaptureService extends android.app.Service {
     ctor public ContentCaptureService();
+    method public final void disableContentCaptureServices();
     method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData);
     method public void onConnected();
     method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
@@ -2110,7 +2125,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -2699,7 +2715,8 @@
   }
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
     field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
     field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
@@ -2711,20 +2728,18 @@
 package android.view.contentcapture {
 
   public final class ContentCaptureContext implements android.os.Parcelable {
-    method @Nullable public String getAction();
     method @Nullable public android.content.ComponentName getActivityComponent();
     method public int getDisplayId();
-    method @Nullable public android.os.Bundle getExtras();
     method public int getFlags();
     method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
     method public int getTaskId();
-    method @Nullable public android.net.Uri getUri();
     field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
     field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
   }
 
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -2733,6 +2748,7 @@
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
@@ -2742,8 +2758,15 @@
 
   public final class ContentCaptureManager {
     method public boolean isContentCaptureFeatureEnabled();
-    method public void setContentCaptureFeatureEnabled(boolean);
+    field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
+    field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
+    field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
+    field public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
     field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled";
+    field public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = "text_change_flush_frequency";
+    field public static final int LOGGING_LEVEL_DEBUG = 1; // 0x1
+    field public static final int LOGGING_LEVEL_OFF = 0; // 0x0
+    field public static final int LOGGING_LEVEL_VERBOSE = 2; // 0x2
   }
 
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 46917e4..a6c7cae 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -110,13 +110,30 @@
     } else {
         mShuttingDown = true;
     }
+    ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
+}
+
+BootAnimation::~BootAnimation() {
+    if (mAnimation != nullptr) {
+        releaseAnimation(mAnimation);
+        mAnimation = nullptr;
+    }
+    ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
 }
 
 void BootAnimation::onFirstRef() {
     status_t err = mSession->linkToComposerDeath(this);
     SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
     if (err == NO_ERROR) {
-        run("BootAnimation", PRIORITY_DISPLAY);
+        // Load the animation content -- this can be slow (eg 200ms)
+        // called before waitForSurfaceFlinger() in main() to avoid wait
+        ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
+        preloadAnimation();
+        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
     }
 }
 
@@ -306,6 +323,20 @@
     mFlingerSurface = s;
     mTargetInset = -1;
 
+    return NO_ERROR;
+}
+
+bool BootAnimation::preloadAnimation() {
+    findBootAnimationFile();
+    if (!mZipFileName.isEmpty()) {
+        mAnimation = loadAnimation(mZipFileName);
+        return (mAnimation != nullptr);
+    }
+
+    return false;
+}
+
+void BootAnimation::findBootAnimationFile() {
     // If the device has encryption turned on or is in process
     // of being encrypted we show the encrypted boot animation.
     char decrypt[PROPERTY_VALUE_MAX];
@@ -320,7 +351,7 @@
         for (const char* f : encryptedBootFiles) {
             if (access(f, R_OK) == 0) {
                 mZipFileName = f;
-                return NO_ERROR;
+                return;
             }
         }
     }
@@ -332,10 +363,9 @@
     for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
         if (access(f, R_OK) == 0) {
             mZipFileName = f;
-            return NO_ERROR;
+            return;
         }
     }
-    return NO_ERROR;
 }
 
 bool BootAnimation::threadLoop()
@@ -790,8 +820,6 @@
         }
     }
 
-    mCallbacks->init(animation.parts);
-
     zip->endIteration(cookie);
 
     return true;
@@ -799,13 +827,25 @@
 
 bool BootAnimation::movie()
 {
-    Animation* animation = loadAnimation(mZipFileName);
-    if (animation == NULL)
+    if (mAnimation == nullptr) {
+        mAnimation = loadAnimation(mZipFileName);
+    }
+
+    if (mAnimation == nullptr)
         return false;
 
+    // mCallbacks->init() may get called recursively,
+    // this loop is needed to get the same results
+    for (const Animation::Part& part : mAnimation->parts) {
+        if (part.animation != nullptr) {
+            mCallbacks->init(part.animation->parts);
+        }
+    }
+    mCallbacks->init(mAnimation->parts);
+
     bool anyPartHasClock = false;
-    for (size_t i=0; i < animation->parts.size(); i++) {
-        if(validClock(animation->parts[i])) {
+    for (size_t i=0; i < mAnimation->parts.size(); i++) {
+        if(validClock(mAnimation->parts[i])) {
             anyPartHasClock = true;
             break;
         }
@@ -846,7 +886,7 @@
     bool clockFontInitialized = false;
     if (mClockEnabled) {
         clockFontInitialized =
-            (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
+            (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
         mClockEnabled = clockFontInitialized;
     }
 
@@ -855,7 +895,7 @@
         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
     }
 
-    playAnimation(*animation);
+    playAnimation(*mAnimation);
 
     if (mTimeCheckThread != nullptr) {
         mTimeCheckThread->requestExit();
@@ -863,10 +903,11 @@
     }
 
     if (clockFontInitialized) {
-        glDeleteTextures(1, &animation->clockFont.texture.name);
+        glDeleteTextures(1, &mAnimation->clockFont.texture.name);
     }
 
-    releaseAnimation(animation);
+    releaseAnimation(mAnimation);
+    mAnimation = nullptr;
 
     return false;
 }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 19616cb..dc19fb0 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -115,6 +115,7 @@
     };
 
     explicit BootAnimation(sp<Callbacks> callbacks);
+    virtual ~BootAnimation();
 
     sp<SurfaceComposerClient> session() const;
 
@@ -155,6 +156,8 @@
     void releaseAnimation(Animation*) const;
     bool parseAnimationDesc(Animation&);
     bool preloadZip(Animation &animation);
+    void findBootAnimationFile();
+    bool preloadAnimation();
 
     void checkExit();
 
@@ -182,6 +185,7 @@
     SortedVector<String8> mLoadedFiles;
     sp<TimeCheckThread> mTimeCheckThread = nullptr;
     sp<Callbacks> mCallbacks;
+    Animation* mAnimation = nullptr;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 1b3c32b..469c964 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,7 +2,6 @@
     class core animation
     user graphics
     group graphics audio
-    updatable
     disabled
     oneshot
     writepid /dev/stune/top-app/tasks
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index a52a5e9..6c7b3e5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -44,14 +44,16 @@
         sp<ProcessState> proc(ProcessState::self());
         ProcessState::self()->startThreadPool();
 
+        // create the boot animation object (may take up to 200ms for 2MB zip)
+        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+
         waitForSurfaceFlinger();
 
-        // create the boot animation object
-        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+        boot->run("BootAnimation", PRIORITY_DISPLAY);
+
         ALOGV("Boot animation set up. Joining pool.");
 
         IPCThreadState::self()->joinThreadPool();
     }
-    ALOGV("Boot animation exit");
     return 0;
 }
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index ea7274f..4a66715 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -24,6 +24,7 @@
   const int POLICY_SYSTEM_PARTITION = 0x00000002;
   const int POLICY_VENDOR_PARTITION = 0x00000004;
   const int POLICY_PRODUCT_PARTITION = 0x00000008;
+  const int POLICY_SIGNATURE = 0x00000010;
 
   @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
   boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 99b5f0f..ec498ff 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -284,7 +284,7 @@
 
 bool CheckOverlayable(const LoadedPackage& target_package,
                       const utils::OverlayManifestInfo& overlay_info,
-                      const PolicyBitmask& fulfilled_polices, const ResourceId& resid) {
+                      const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
   const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
   if (overlayable_info == nullptr) {
     // If the resource does not have an overlayable definition, allow the resource to be overlaid.
@@ -299,7 +299,7 @@
   }
 
   // Enforce policy restrictions if the resource is declared as overlayable.
-  return (overlayable_info->policy_flags & fulfilled_polices) != 0;
+  return (overlayable_info->policy_flags & fulfilled_policies) != 0;
 }
 
 std::unique_ptr<const Idmap> Idmap::FromApkAssets(
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
index 0f87ef0..6649288 100644
--- a/cmds/idmap2/libidmap2/Policies.cpp
+++ b/cmds/idmap2/libidmap2/Policies.cpp
@@ -35,6 +35,7 @@
     {"product", PolicyFlags::POLICY_PRODUCT_PARTITION},
     {"system", PolicyFlags::POLICY_SYSTEM_PARTITION},
     {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION},
+    {"signature", PolicyFlags::POLICY_SIGNATURE},
 };
 }  // namespace
 
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 0e0e25f..9a0412e 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -129,28 +129,31 @@
   success = LoadedIdmap::Lookup(header, 0x0008, &entry);  // string/policy_system_vendor
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x0009, &entry);  // string/str1
+  success = LoadedIdmap::Lookup(header, 0x0009, &entry);  // string/policy_signature
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x000a, &entry);  // string/str1
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0000);
 
-  success = LoadedIdmap::Lookup(header, 0x000a, &entry);  // string/str2
+  success = LoadedIdmap::Lookup(header, 0x000b, &entry);  // string/str2
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x000b, &entry);  // string/str3
+  success = LoadedIdmap::Lookup(header, 0x000c, &entry);  // string/str3
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0001);
 
-  success = LoadedIdmap::Lookup(header, 0x000c, &entry);  // string/str4
+  success = LoadedIdmap::Lookup(header, 0x000d, &entry);  // string/str4
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0002);
 
-  success = LoadedIdmap::Lookup(header, 0x000d, &entry);  // string/x
+  success = LoadedIdmap::Lookup(header, 0x000e, &entry);  // string/x
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x000e, &entry);  // string/y
+  success = LoadedIdmap::Lookup(header, 0x000f, &entry);  // string/y
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x000f, &entry);  // string/z
+  success = LoadedIdmap::Lookup(header, 0x0010, &entry);  // string/z
   ASSERT_FALSE(success);
 }
 
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 8514e12..2e85eb6 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -39,12 +39,13 @@
                             [](unsigned char type ATTRIBUTE_UNUSED,
                                const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 6U);
+  ASSERT_EQ(v->size(), 7U);
   ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
                                                              root + "/.",
                                                              root + "/..",
                                                              root + "/overlay",
                                                              root + "/target",
+                                                             root + "/signature-overlay",
                                                              root + "/system-overlay",
                                                              root + "/system-overlay-invalid",
                                                          }));
@@ -56,15 +57,22 @@
     return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
   });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 9U);
+  ASSERT_EQ(v->size(), 10U);
   ASSERT_EQ(
       std::set<std::string>(v->begin(), v->end()),
       std::set<std::string>(
-          {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
-           root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
-           root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-static-1.apk",
-           root + "/overlay/overlay-static-2.apk", root + "/system-overlay/system-overlay.apk",
-           root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
+          {
+              root + "/target/target.apk",
+              root + "/target/target-no-overlayable.apk",
+              root + "/overlay/overlay.apk",
+              root + "/overlay/overlay-no-name.apk",
+              root + "/overlay/overlay-no-name-static.apk",
+              root + "/overlay/overlay-static-1.apk",
+              root + "/overlay/overlay-static-2.apk",
+              root + "/signature-overlay/signature-overlay.apk",
+              root + "/system-overlay/system-overlay.apk",
+              root + "/system-overlay-invalid/system-overlay-invalid.apk"
+          }));
 }
 
 TEST(FileUtilsTests, ReadFile) {
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 1216f9ec..a6a2ada 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -132,9 +132,9 @@
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
   ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f020009 -> 0x7f020000 string/str1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020001 string/str3"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020002 string/str4"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020000 string/str1"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020001 string/str3"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000d -> 0x7f020002 string/str4"), std::string::npos);
   ASSERT_EQ(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
 
   // clang-format off
@@ -286,7 +286,7 @@
                           "lookup",
                           "--idmap-path", GetIdmapPath(),
                           "--config", "",
-                          "--resid", "0x7f020009"});  // string/str1
+                          "--resid", "0x7f02000a"});  // string/str1
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index b40521f..53ec03b 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -191,8 +191,8 @@
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
   ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
-  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xdd53ca29);
-  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xa71ccd77);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xd513ca1b);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -217,7 +217,7 @@
   ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetEntryCount(), 4U);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 9U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 10U);
   ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
   ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
@@ -254,11 +254,76 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 3U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 4U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 6U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0001U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0002U);  // string/policy_system_vendor
+}
+
+TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE;
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           policy_flags, /* enforce_overlayable */ true, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 1U);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 7U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature
+}
+
+TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC;
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           policy_flags, /* enforce_overlayable */ true, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 0U);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 0U); // can't overlay, so contains nothing
 }
 
 // Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
@@ -292,11 +357,12 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 3U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 4U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 6U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0003U);  // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(1), 0x0004U);  // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(2), 0x0005U);  // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0005U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0006U);  // string/policy_system_vendor
 }
 
 // Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled.
@@ -330,14 +396,15 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 6U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 7U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/not_overlayable
   ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/other
   ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_product
-  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);  // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_signature
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(6), 0x0006U);  // string/policy_system_vendor
 }
 
 // The resources of APKs that do not include an overlayable declaration should not restrict what
@@ -371,14 +438,15 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 6U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 7U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/not_overlayable
   ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/other
   ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_product
   ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);  // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/string/policy_signature
+  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(6), 0x0006U);  // string/policy_system_vendor
 }
 
 // The resources of APKs that do not include an overlayable declaration should not restrict what
@@ -418,7 +486,7 @@
   ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetEntryCount(), 4U);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 9U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 10U);
   ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
   ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index a5588c3..7ec13ed 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -52,8 +52,8 @@
 
   ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
   ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
-  ASSERT_NE(stream.str().find("00000008: dd53ca29  target crc\n"), std::string::npos);
-  ASSERT_NE(stream.str().find("0000000c: a71ccd77  overlay crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000008: d513ca1b  target crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000000c: 8635c2ed  overlay crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f010000 -> 0x7f010000 integer/int1\n"),
             std::string::npos);
 }
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
old mode 100644
new mode 100755
index e60da80..e879f44
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk"
+FRAMEWORK_RES_APK="${ANDROID_BUILD_TOP}/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk"
 
 aapt2 compile --dir res -o compiled.flata
 
diff --git a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml
new file mode 100644
index 0000000..5dacebd
--- /dev/null
+++ b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="test.overlay.system">
+    <overlay
+        android:targetPackage="test.target"
+        android:targetName="TestResources"/>
+</manifest>
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.mk b/cmds/idmap2/tests/data/signature-overlay/build
old mode 100644
new mode 100755
similarity index 60%
rename from tools/aapt2/integration-tests/AutoVersionTest/Android.mk
rename to cmds/idmap2/tests/data/signature-overlay/build
index 03cce35..fdd8301
--- a/tools/aapt2/integration-tests/AutoVersionTest/Android.mk
+++ b/cmds/idmap2/tests/data/signature-overlay/build
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright (C) 2019 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.
@@ -12,13 +11,16 @@
 # 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.
-#
 
-LOCAL_PATH := $(call my-dir)
+FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
 
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptAutoVersionTest
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_PACKAGE)
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifest.xml \
+    -o signature-overlay.apk \
+    compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml
new file mode 100644
index 0000000..59e7d8e
--- /dev/null
+++ b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- This overlay will fulfill the policy "signature". This allows it overlay the
+     following resources. -->
+    <string name="policy_signature">policy_signature</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk
new file mode 100644
index 0000000..b2c490d
--- /dev/null
+++ b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build
old mode 100644
new mode 100755
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index af1bea1..0270400 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -22,6 +22,7 @@
 
     <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. -->
     <string name="policy_product">policy_product</string>
+    <string name="policy_signature">policy_signature</string>
 
     <!-- Requests to overlay a resource that is not declared as overlayable. -->
     <string name="not_overlayable">not_overlayable</string>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index 710ed90..9448939 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build
old mode 100644
new mode 100755
diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build
old mode 100644
new mode 100755
index 137ddb5..e6df742
--- a/cmds/idmap2/tests/data/target/build
+++ b/cmds/idmap2/tests/data/target/build
@@ -17,5 +17,5 @@
 rm compiled.flata
 
 aapt2 compile res/values/values.xml -o .
-aapt2 link --manifest AndroidManifest.xml -A assets -o target_no_overlayable.apk values_values.arsc.flat
+aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat
 rm values_values.arsc.flat
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 02d2563..0bf83fa 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -15,20 +15,12 @@
 -->
 <resources>
 <overlayable name="TestResources">
-    <!-- Publicly overlayable resources -->
-    <item type="string" name="a" />
-    <item type="string" name="b" />
-    <item type="string" name="c" />
-    <item type="string" name="str1" />
-    <item type="string" name="str2" />
-    <item type="string" name="str3" />
-    <item type="string" name="str4" />
-    <item type="string" name="x" />
-    <item type="string" name="y" />
-    <item type="string" name="z" />
-    <item type="integer" name="int1" />
+    <!-- Resources with signature restrictions -->
+    <policy type="signature">
+        <item type="string" name="policy_signature" />
+    </policy>
 
-    <!-- Resources with partition restrictins -->
+    <!-- Resources with partition restrictions -->
     <policy type="system">
         <item type="string" name="policy_system" />
     </policy>
@@ -41,12 +33,26 @@
         <item type="string" name="policy_product" />
     </policy>
 
+    <!-- Resources publicly overlayable -->
     <policy type="public">
         <item type="string" name="policy_public" />
+        <item type="string" name="a" />
+        <item type="string" name="b" />
+        <item type="string" name="c" />
+        <item type="string" name="str1" />
+        <item type="string" name="str2" />
+        <item type="string" name="str3" />
+        <item type="string" name="str4" />
+        <item type="string" name="x" />
+        <item type="string" name="y" />
+        <item type="string" name="z" />
+        <item type="integer" name="int1" />
     </policy>
 </overlayable>
 
 <overlayable name="OtherResources">
-    <item type="string" name="other" />
+    <policy type="public">
+        <item type="string" name="other" />
+    </policy>
 </overlayable>
 </resources>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index 0d337f3..edd53f4 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -33,6 +33,7 @@
     <string name="policy_system_vendor">policy_system_vendor</string>
     <string name="policy_product">policy_product</string>
     <string name="policy_public">policy_public</string>
+    <string name="policy_signature">policy_signature</string>
 
     <item type="string" name="other" />
 </resources>
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 8676cbb..908b54a 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index ecbe875..da3c1ae 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index faf2053..f408655 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -76,6 +76,7 @@
         "src/external/SubsystemSleepStatePuller.cpp",
         "src/external/PowerStatsPuller.cpp",
         "src/external/ResourceHealthManagerPuller.cpp",
+        "src/external/TrainInfoPuller.cpp",
         "src/external/StatsPullerManager.cpp",
         "src/external/puller_util.cpp",
         "src/logd/LogEvent.cpp",
@@ -238,6 +239,7 @@
         "tests/guardrail/StatsdStats_test.cpp",
         "tests/metrics/metrics_test_helper.cpp",
         "tests/statsd_test_util.cpp",
+        "tests/storage/StorageManager_test.cpp",
         "tests/e2e/WakelockDuration_e2e_test.cpp",
         "tests/e2e/MetricActivation_e2e_test.cpp",
         "tests/e2e/MetricConditionLink_e2e_test.cpp",
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 067b6ed..cca6d52 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -367,7 +367,8 @@
     sp<AlarmMonitor> periodicAlarmMonitor;
     sp<StatsLogProcessor> processor =
             new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; });
+                                  timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; },
+                                  [](const int&, const vector<int64_t>&) { return true; });
     processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
     return processor;
 }
@@ -393,4 +394,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 80ed807..13f5c8a 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -19,6 +19,7 @@
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
 #include "math.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -122,6 +123,24 @@
     return false;
 }
 
+int32_t getUidIfExists(const FieldValue& value) {
+    bool isUid = false;
+    // the field is uid field if the field is the uid field in attribution node or marked as
+    // is_uid in atoms.proto
+    if (isAttributionUidField(value)) {
+        isUid = true;
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto
+            isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
+                    value.mValue.getType() == INT;
+        }
+    }
+
+    return isUid ? value.mValue.int_value : -1;
+}
+
 bool isAttributionUidField(const Field& field, const Value& value) {
     int f = field.getField() & 0xff007f;
     if (f == 0x10001 && value.getType() == INT) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index a5d00ac..6729e05 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -386,6 +386,9 @@
 
 bool isAttributionUidField(const FieldValue& value);
 
+/* returns uid if the field is uid field, or -1 if the field is not a uid field */
+int getUidIfExists(const FieldValue& value);
+
 void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
 
 bool isAttributionUidField(const Field& field, const Value& value);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index dd18bd4..653ef2e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -88,12 +88,15 @@
                                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                      const sp<AlarmMonitor>& periodicAlarmMonitor,
                                      const int64_t timeBaseNs,
-                                     const std::function<bool(const ConfigKey&)>& sendBroadcast)
+                                     const std::function<bool(const ConfigKey&)>& sendBroadcast,
+                                     const std::function<bool(
+                                            const int&, const vector<int64_t>&)>& activateBroadcast)
     : mUidMap(uidMap),
       mPullerManager(pullerManager),
       mAnomalyAlarmMonitor(anomalyAlarmMonitor),
       mPeriodicAlarmMonitor(periodicAlarmMonitor),
       mSendBroadcast(sendBroadcast),
+      mSendActivationBroadcast(activateBroadcast),
       mTimeBaseNs(timeBaseNs),
       mLargestTimestampSeen(0),
       mLastTimestampSeen(0) {
@@ -223,11 +226,73 @@
         mapIsolatedUidToHostUidIfNecessaryLocked(event);
     }
 
+    std::unordered_set<int> uidsWithActiveConfigsChanged;
+    std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid;
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
+        int uid = pair.first.GetUid();
+        int64_t configId = pair.first.GetId();
+        bool isPrevActive = pair.second->isActive();
         pair.second->onLogEvent(*event);
+        bool isCurActive = pair.second->isActive();
+        // Map all active configs by uid.
+        if (isCurActive) {
+            auto activeConfigs = activeConfigsPerUid.find(uid);
+            if (activeConfigs != activeConfigsPerUid.end()) {
+                activeConfigs->second.push_back(configId);
+            } else {
+                vector<int64_t> newActiveConfigs;
+                newActiveConfigs.push_back(configId);
+                activeConfigsPerUid[uid] = newActiveConfigs;
+            }
+        }
+        // The activation state of this config changed.
+        if (isPrevActive != isCurActive) {
+            VLOG("Active status changed for uid  %d", uid);
+            uidsWithActiveConfigsChanged.insert(uid);
+            StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive);
+        }
         flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
     }
+
+    for (int uid : uidsWithActiveConfigsChanged) {
+        // Send broadcast so that receivers can pull data.
+        auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid);
+        if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
+            if (currentTimestampNs - lastBroadcastTime->second <
+                    StatsdStats::kMinActivationBroadcastPeriodNs) {
+                VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
+                return;
+            }
+        }
+        auto activeConfigs = activeConfigsPerUid.find(uid);
+        if (activeConfigs != activeConfigsPerUid.end()) {
+            if (mSendActivationBroadcast(uid, activeConfigs->second)) {
+                VLOG("StatsD sent activation notice for uid %d", uid);
+                mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+            }
+        } else {
+            std::vector<int64_t> emptyActiveConfigs;
+            if (mSendActivationBroadcast(uid, emptyActiveConfigs)) {
+                VLOG("StatsD sent EMPTY activation notice for uid %d", uid);
+                mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+            }
+        }
+    }
+}
+
+void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    GetActiveConfigsLocked(uid, outActiveConfigs);
+}
+
+void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) {
+    outActiveConfigs.clear();
+    for (auto& pair : mMetricsManagers) {
+        if (pair.first.GetUid() == uid && pair.second->isActive()) {
+            outActiveConfigs.push_back(pair.first.GetId());
+        }
+    }
 }
 
 void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
@@ -444,6 +509,18 @@
 
     mLastBroadcastTimes.erase(key);
 
+    int uid = key.GetUid();
+    bool lastConfigForUid = true;
+    for (auto it : mMetricsManagers) {
+        if (it.first.GetUid() == uid) {
+            lastConfigForUid = false;
+            break;
+        }
+    }
+    if (lastConfigForUid) {
+        mLastActivationBroadcastTimes.erase(uid);
+    }
+
     if (mMetricsManagers.empty()) {
         mPullerManager->ForceClearPullerCache();
     }
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index caf1a71..ea9c6e7 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -49,7 +49,9 @@
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
                       const int64_t timeBaseNs,
-                      const std::function<bool(const ConfigKey&)>& sendBroadcast);
+                      const std::function<bool(const ConfigKey&)>& sendBroadcast,
+                      const std::function<bool(const int&,
+                                               const vector<int64_t>&)>& sendActivationBroadcast);
     virtual ~StatsLogProcessor();
 
     void OnLogEvent(LogEvent* event);
@@ -60,6 +62,8 @@
 
     size_t GetMetricsSize(const ConfigKey& key) const;
 
+    void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs);
+
     void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
                       const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, vector<uint8_t>* outData);
@@ -125,6 +129,9 @@
 
     std::unordered_map<ConfigKey, long> mLastBroadcastTimes;
 
+    // Last time we sent a broadcast to this uid that the active configs had changed.
+    std::unordered_map<int, long> mLastActivationBroadcastTimes;
+
     // Tracks when we last checked the bytes consumed for each config key.
     std::unordered_map<ConfigKey, long> mLastByteSizeTimes;
 
@@ -144,6 +151,8 @@
     void OnConfigUpdatedLocked(
         const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
 
+    void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
+
     void WriteDataToDiskLocked(const DumpReportReason dumpReportReason);
     void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
                                const DumpReportReason dumpReportReason);
@@ -174,6 +183,10 @@
     // to retrieve the stored data.
     std::function<bool(const ConfigKey& key)> mSendBroadcast;
 
+    // Function used to send a broadcast so that receiver can be notified of which configs
+    // are currently active.
+    std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast;
+
     const int64_t mTimeBaseNs;
 
     // Largest timestamp of the events that we have processed.
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index bd21a95..c542b62 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -31,6 +31,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
 #include <dirent.h>
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
 #include <private/android_filesystem_config.h>
@@ -47,6 +48,7 @@
 
 using android::base::StringPrintf;
 using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
 
 namespace android {
@@ -62,6 +64,8 @@
 
 // for StatsDataDumpProto
 const int FIELD_ID_REPORTS_LIST = 1;
+// for TrainInfo experiment id serialization
+const int FIELD_ID_EXPERIMENT_ID = 1;
 
 static binder::Status ok() {
     return binder::Status::ok();
@@ -155,7 +159,7 @@
            }
 
       }))  {
-    mUidMap = new UidMap();
+    mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
@@ -176,6 +180,21 @@
                     sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
                     return true;
                 }
+            },
+            [this](const int& uid, const vector<int64_t>& activeConfigs) {
+                auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+                sp<IStatsCompanionService> sc = getStatsCompanionService();
+                if (sc == nullptr) {
+                    VLOG("Could not access statsCompanion");
+                    return false;
+                } else if (receiver == nullptr) {
+                    VLOG("Could not find receiver for uid %d", uid);
+                    return false;
+                } else {
+                    sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs);
+                    VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
+                    return true;
+                }
             });
 
     mConfigManager->AddListener(mProcessor);
@@ -357,6 +376,9 @@
         if (!args[0].compare(String8("print-logs"))) {
             return cmd_print_logs(out, args);
         }
+        if (!args[0].compare(String8("send-active-configs"))) {
+            return cmd_trigger_active_config_broadcast(out, args);
+        }
         if (!args[0].compare(String8("data-subscribe"))) {
             if (mShellSubscriber == nullptr) {
                 mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
@@ -449,6 +471,19 @@
     dprintf(out, "  NAME          The name of the configuration\n");
     dprintf(out, "\n");
     dprintf(out, "\n");
+    dprintf(out,
+            "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] "
+            "[NAME1] [NAME2] [NAME3..]\n");
+    dprintf(out, "  Send a broadcast that informs the subscriber of the current active configs.\n");
+    dprintf(out, "  --uid=UID     The uid of the configurations. It is only possible to pass\n");
+    dprintf(out, "                the UID parameter on eng builds. If UID is omitted the\n");
+    dprintf(out, "                calling uid is used.\n");
+    dprintf(out, "  --configs     Send the list of configs in the name list instead of\n");
+    dprintf(out, "                the currently active configs\n");
+    dprintf(out, "  NAME LIST     List of configuration names to be included in the broadcast.\n");
+
+    dprintf(out, "\n");
+    dprintf(out, "\n");
     dprintf(out, "usage: adb shell cmd stats print-stats\n");
     dprintf(out, "  Prints some basic stats.\n");
     dprintf(out, "  --proto       Print proto binary instead of string format.\n");
@@ -499,6 +534,59 @@
     return NO_ERROR;
 }
 
+status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) {
+    const int argCount = args.size();
+    int uid;
+    vector<int64_t> configIds;
+    if (argCount == 1) {
+        // Automatically pick the uid and send a broadcast that has no active configs.
+        uid = IPCThreadState::self()->getCallingUid();
+        mProcessor->GetActiveConfigs(uid, configIds);
+    } else {
+        int curArg = 1;
+        if(args[curArg].find("--uid=") == 0) {
+            string uidArgStr(args[curArg].c_str());
+            string uidStr = uidArgStr.substr(6);
+            if (!getUidFromString(uidStr.c_str(), uid)) {
+                dprintf(out, "Invalid UID. Note that the config can only be set for "
+                             "other UIDs on eng or userdebug builds.\n");
+                return UNKNOWN_ERROR;
+            }
+            curArg++;
+        } else {
+            uid = IPCThreadState::self()->getCallingUid();
+        }
+        if (curArg == argCount || args[curArg] != "--configs") {
+            VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
+            mProcessor->GetActiveConfigs(uid, configIds);
+        } else {
+            // Flag specified, use the given list of configs.
+            curArg++;
+            for (int i = curArg; i < argCount; i++) {
+                char* endp;
+                int64_t configID = strtoll(args[i].c_str(), &endp, 10);
+                if (endp == args[i].c_str() || *endp != '\0') {
+                    dprintf(out, "Error parsing config ID.\n");
+                    return UNKNOWN_ERROR;
+                }
+                VLOG("Adding config id %ld", static_cast<long>(configID));
+                configIds.push_back(configID);
+            }
+        }
+    }
+    auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+    sp<IStatsCompanionService> sc = getStatsCompanionService();
+    if (sc == nullptr) {
+        VLOG("Could not access statsCompanion");
+    } else if (receiver == nullptr) {
+        VLOG("Could not find receiver for uid %d", uid);
+    } else {
+        sc->sendActiveConfigsChangedBroadcast(receiver, configIds);
+        VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
+    }
+    return NO_ERROR;
+}
+
 status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) {
     const int argCount = args.size();
     if (argCount >= 2) {
@@ -762,7 +850,10 @@
 }
 
 bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
-    const char* s = args[uidArgIndex].c_str();
+    return getUidFromString(args[uidArgIndex].c_str(), uid);
+}
+
+bool StatsService::getUidFromString(const char* s, int32_t& uid) {
     if (*s == '\0') {
         return false;
     }
@@ -998,8 +1089,13 @@
     ENFORCE_DUMP_AND_USAGE_STATS(packageName);
 
     IPCThreadState* ipc = IPCThreadState::self();
-    mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender);
-    //TODO: Return the list of configs that are already active
+    int uid = ipc->getCallingUid();
+    mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender);
+    if (output != nullptr) {
+        mProcessor->GetActiveConfigs(uid, *output);
+    } else {
+        ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
+    }
     return Status::ok();
 }
 
@@ -1075,6 +1171,56 @@
     return Status::ok();
 }
 
+Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName,
+                                                    int64_t trainVersionCode, int options,
+                                                    int32_t state,
+                                                    const std::vector<int64_t>& experimentIds) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    // For testing
+    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
+        return ok();
+    }
+
+    // Caller must be granted these permissions
+    if (!checkCallingPermission(String16(kPermissionDump))) {
+        return exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
+    }
+    if (!checkCallingPermission(String16(kPermissionUsage))) {
+        return exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
+    }
+    // TODO: add verifier permission
+
+    userid_t userId = multiuser_get_user_id(uid);
+
+    bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING;
+    bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
+    bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+
+    ProtoOutputStream proto;
+    for (const auto& expId : experimentIds) {
+        proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
+                    (long long)expId);
+    }
+
+    vector<uint8_t> buffer;
+    buffer.resize(proto.size());
+    size_t pos = 0;
+    auto iter = proto.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+    LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging,
+                   rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId);
+    mProcessor->OnLogEvent(&event);
+    StorageManager::writeTrainInfo(trainVersionCode, buffer);
+    return Status::ok();
+}
+
 hardware::Return<void> StatsService::reportSpeakerImpedance(
         const SpeakerImpedance& speakerImpedance) {
     LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 941ed46..d24565a 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -31,6 +31,7 @@
 #include <android/frameworks/stats/1.0/types.h>
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
+#include <android/os/IStatsManager.h>
 #include <binder/IResultReceiver.h>
 #include <utils/Looper.h>
 
@@ -186,6 +187,13 @@
     virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
 
     /**
+     * Binder call to log BinaryPushStateChanged atom.
+     */
+    virtual Status sendBinaryPushStateChangedAtom(
+            const android::String16& trainName, int64_t trainVersionCode, int options,
+            int32_t state, const std::vector<int64_t>& experimentIds) override;
+
+    /**
      * Binder call to get SpeakerImpedance atom.
      */
     virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
@@ -275,6 +283,12 @@
      */
     status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args);
 
+
+    /**
+     * Trigger an active configs changed broadcast.
+     */
+    status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args);
+
     /**
      * Handle the config sub-command.
      */
@@ -341,6 +355,15 @@
     bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
 
     /**
+     * Writes the value of uidSting into uid.
+     * Returns whether the uid is reasonable (type uid_t) and whether
+     * 1. it is equal to the calling uid, or
+     * 2. the device is mEngBuild, or
+     * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
+     */
+     bool getUidFromString(const char* uidString, int32_t& uid);
+
+    /**
      * Adds a configuration after checking permissions and obtaining UID from binder call.
      */
     bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 8d73699..019a9f7 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -78,8 +78,8 @@
     }
     if (!mSubscriptions.empty()) {
         VLOG("AlarmTracker triggers the subscribers.");
-        triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
-                           mSubscriptions);
+        triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
+                           0 /* metricValue N/A */, mConfigKey, mSubscriptions);
     }
     firedAlarms.erase(mInternalAlarm);
     mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ee111cd..d1dcb5df 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -207,7 +207,8 @@
            getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+                                    const MetricDimensionKey& key, int64_t metricValue) {
     // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
     // real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
@@ -225,7 +226,7 @@
     if (!mSubscriptions.empty()) {
         ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.",
                 mAlert.id(), key.toString().c_str());
-        informSubscribers(key);
+        informSubscribers(key, metricId, metricValue);
     } else {
         ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
     }
@@ -238,11 +239,11 @@
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
-                                             const int64_t& currBucketNum,
+                                             const int64_t& currBucketNum, int64_t metricId,
                                              const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
-        declareAnomaly(timestampNs, key);
+        declareAnomaly(timestampNs, metricId, key, currentBucketValue);
     }
 }
 
@@ -255,8 +256,9 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
-    triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
+                                       int64_t metricValue) {
+    triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 927e2df..e941473 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -67,14 +67,16 @@
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key);
+    void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+                        int64_t metricValue);
 
     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     // declares an anomaly and informs relevant subscribers.
     // Also advances to currBucketNum-1.
     void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
-                                 const MetricDimensionKey& key, const int64_t& currentBucketValue);
+                                 int64_t metricId, const MetricDimensionKey& key,
+                                 const int64_t& currentBucketValue);
 
     // Init the AlarmMonitor which is shared across anomaly trackers.
     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -176,7 +178,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
-    void informSubscribers(const MetricDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3acfd17..2b56810 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -65,7 +65,9 @@
 
     // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
     if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
-        declareAnomaly(timestampNs, dimensionKey);
+        declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
+                       mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
+                               itr->second->timestampSec);
     }
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->remove(itr->second);
@@ -100,7 +102,9 @@
 
     // Now declare each of these alarms to have fired.
     for (const auto& kv : matchedAlarms) {
-        declareAnomaly(timestampNs, kv.first);
+        declareAnomaly(
+                timestampNs, mAlert.metric_id(), kv.first,
+                mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
         mAlarms.erase(kv.first);
         firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 6b46b8b..548a686 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -30,9 +30,8 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
+void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                        int64_t metricValue, const ConfigKey& configKey,
                         const std::vector<Subscription>& subscriptions) {
     VLOG("informSubscribers called.");
     if (subscriptions.empty()) {
@@ -50,13 +49,14 @@
         }
         switch (subscription.subscriber_information_case()) {
             case Subscription::SubscriberInformationCase::kIncidentdDetails:
-                if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+                if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
+                                            dimensionKey, metricValue, configKey)) {
                     ALOGW("Failed to generate incident report.");
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            subscription.id(), rule_id, configKey)) {
+                                                            subscription.id(), ruleId, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
@@ -66,7 +66,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfprofdDetails:
                 if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(),
-                                                             rule_id, configKey)) {
+                                                             ruleId, configKey)) {
                     ALOGW("Failed to generate perfprofd traces.");
                 }
                 break;
@@ -76,7 +76,6 @@
     }
 }
 
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
index dba8981..1df3c89 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.h
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -24,10 +24,9 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
-                        const std::vector<Subscription>& subscriptions);
+void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+                        const MetricDimensionKey& dimensionKey, int64_t metricValue,
+                        const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index da7e4da..d78647e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -31,6 +31,7 @@
 import "frameworks/base/core/proto/android/bluetooth/smp/enums.proto";
 import "frameworks/base/core/proto/android/debug/enums.proto";
 import "frameworks/base/core/proto/android/hardware/biometrics/enums.proto";
+import "frameworks/base/core/proto/android/hardware/sensor/assist/enums.proto";
 import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
@@ -241,6 +242,11 @@
         BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
         DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
         BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
+        AssistGestureStageReported assist_gesture_stage_reported = 174;
+        AssistGestureFeedbackReported assist_gesture_feedback_reported = 175;
+        AssistGestureProgressReported assist_gesture_progress_reported = 176;
+        TouchGestureClassified touch_gesture_classified = 177;
+        HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
     }
 
     // Pulled events will start at field 10000.
@@ -298,6 +304,8 @@
         RoleHolder role_holder = 10049;
         DangerousPermissionState dangerous_permission_state = 10050;
         TrainInfo train_info = 10051;
+        TimeZoneDataInfo time_zone_data_info = 10052;
+        SDCardInfo sdcard_info = 10053;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2403,6 +2411,37 @@
 }
 
 /**
+ * Logs gesture classification and timing information for touch events.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/android/view/GestureDetector.java
+ *   frameworks/base/core/java/android/view/View.java
+ */
+message TouchGestureClassified {
+    // The source of the classification (e.g. Java class name).
+    optional string source = 1;
+
+    enum Classification {
+        UNKNOWN_CLASSIFICATION = 0;
+        SINGLE_TAP = 1;
+        DOUBLE_TAP = 2;
+        LONG_PRESS = 3;
+        DEEP_PRESS = 4;
+        SCROLL = 5;
+    }
+    // The classification of the gesture.
+    optional Classification classification = 2;
+
+    // The interval from the start of a touch event stream until the
+    // classification was made.
+    optional int32 latency_millis = 3;
+
+    // The distance from the location of the first touch event to the
+    // location of the touch event when the classification was made.
+    optional float displacement_px = 4;
+}
+
+/**
  * Logs that a setting was updated.
  * Logged from:
  *   frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -3152,6 +3191,13 @@
     optional int64 order_id = 2;
 }
 
+/**
+ * Potential experiment ids that goes with a train install.
+ */
+message TrainExperimentIds {
+    repeated int64 experiment_id = 1;
+}
+
 /*
  * Logs when a binary push state changes.
  * Logged by the installer via public api.
@@ -3178,8 +3224,14 @@
         INSTALL_FAILURE = 6;
         INSTALL_CANCELLED = 7;
         INSTALLER_ROLLBACK_REQUESTED = 8;
+        INSTALLER_ROLLBACK_SUCCESS = 9;
+        INSTALLER_ROLLBACK_FAILURE = 10;
     }
     optional State state = 6;
+    // Possible experiment ids for monitoring this push.
+    optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
+    // user id
+    optional int32 user_id = 8;
 }
 
 /** Represents USB port overheat event. */
@@ -3213,6 +3265,28 @@
     optional int32 cycle_count = 1;
 }
 
+/**
+ * Logs that an SD card is mounted and information about it, its type (public or private) and the
+ * size in bytes.
+ * Pulled from:
+ *   StatsCompanionService
+ */
+
+message SDCardInfo {
+
+    enum Type {
+        UNKNOWN = 0;
+        TYPE_PUBLIC = 1;
+        TYPE_PRIVATE = 2;
+        OTHERS = 3;
+    }
+
+    // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal.
+    optional Type type = 1;
+    // Total size of the sd card in bytes.
+    optional int64 size_bytes = 2;
+}
+
 /*
  * Logs when a connection becomes available and lost.
  * Logged in StatsCompanionService.java
@@ -3265,6 +3339,33 @@
     optional string service_name = 3;
 }
 
+/**
+ * Logs when a hidden API is used.
+ *
+ * Logged from:
+ *     libcore/libart/src/main/java/dalvik/system/VMRuntime.java
+ */
+message HiddenApiUsed {
+    // The uid of the app making the hidden access.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Signature of the method or field accessed.
+    optional string signature = 2;
+
+    enum AccessMethod {
+        NONE = 0;
+        REFLECTION = 1;
+        JNI = 2;
+        LINKING = 3;
+    }
+
+    // Type of access.
+    optional AccessMethod access_method = 3;
+
+    // Whether the access was prevented or not.
+    optional bool access_denied = 4;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -4748,6 +4849,12 @@
 
   // The process state at the time of compaction.
   optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
+
+  // Free ZRAM in kilobytes before compaction.
+  optional int64 before_zram_free_kilobytes = 17;
+
+  // Free ZRAM in kilobytes after compaction.
+  optional int64 after_zram_free_kilobytes = 18;
 }
 
 /**
@@ -5507,13 +5614,6 @@
 }
 
 /**
- * Potential experiment ids that goes with a train install.
- */
-message TrainExperimentIds {
-    repeated int64 experiment_id = 1;
-}
-
-/**
  * Pulls the ongoing mainline install train version code.
  * Pulled from StatsCompanionService
  */
@@ -5522,3 +5622,43 @@
 
     optional TrainExperimentIds train_experiment_id = 2;
 }
+
+/**
+ * Logs the gesture stage changed event.
+ *
+ * Logged from:
+ *   frameworks/base/packages/SystemUI/
+ */
+message AssistGestureStageReported {
+  optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1;
+}
+
+/**
+ * Logs the feedback type.
+ *
+ * Logged from:
+ *   frameworks/base/packages/SystemUI/
+ */
+message AssistGestureFeedbackReported {
+  // Whether or not the gesture was used.
+  optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1;
+}
+
+/**
+ * Logs the progress.
+ *
+ * Logged from:
+ *   frameworks/base/packages/SystemUI/
+ */
+message AssistGestureProgressReported {
+  // [0,100] progress for the assist gesture.
+  optional int32 progress = 1;
+}
+
+/*
+ * Information about the time zone data on a device.
+ */
+message TimeZoneDataInfo {
+    // A version identifier for the data set on device. e.g. "2018i"
+    optional string tzdb_version = 1;
+}
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index aa22333..fc949b4 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -144,10 +144,20 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-        auto uidIt = mConfigs.find(key.GetUid());
+        auto uid = key.GetUid();
+        auto uidIt = mConfigs.find(uid);
         if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
             // Remove from map
             uidIt->second.erase(key);
+
+            // No more configs for this uid, lets remove the active configs callback.
+            if (uidIt->second.empty()) {
+                auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
+                    if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
+                        mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
+                    }
+            }
+
             for (const sp<ConfigListener>& listener : mListeners) {
                 broadcastList.push_back(listener);
             }
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ed72b29..1513834 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -29,10 +29,11 @@
 #include "../statscompanion_util.h"
 #include "PowerStatsPuller.h"
 #include "ResourceHealthManagerPuller.h"
-#include "StatsCompanionServicePuller.h"
 #include "StatsCallbackPuller.h"
+#include "StatsCompanionServicePuller.h"
 #include "StatsPullerManager.h"
 #include "SubsystemSleepStatePuller.h"
+#include "TrainInfoPuller.h"
 #include "statslog.h"
 
 #include <iostream>
@@ -152,7 +153,7 @@
                   new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
         {android::util::TEMPERATURE,
-          {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
         // binder_calls
         {android::util::BINDER_CALLS,
          {.additiveFields = {4, 5, 6, 8, 12},
@@ -231,6 +232,14 @@
         // PermissionState.
         {android::util::DANGEROUS_PERMISSION_STATE,
          {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
+        // TrainInfo.
+        {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
+        // TimeZoneDataInfo.
+        {android::util::TIME_ZONE_DATA_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
+        // SDCardInfo
+        {android::util::SDCARD_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::SDCARD_INFO)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
new file mode 100644
index 0000000..9d09242
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "external/StatsPuller.h"
+
+#include "TrainInfoPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+#include "statslog.h"
+#include "storage/StorageManager.h"
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TrainInfoPuller::TrainInfoPuller() :
+    StatsPuller(android::util::TRAIN_INFO) {
+}
+
+bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    InstallTrainInfo trainInfo;
+    bool ret = StorageManager::readTrainInfo(trainInfo);
+    if (!ret) {
+        ALOGW("Failed to read train info.");
+        return false;
+    }
+    auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+    data->push_back(event);
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h
new file mode 100644
index 0000000..615d023
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads train info from disk.
+ */
+class TrainInfoPuller : public StatsPuller {
+ public:
+  TrainInfoPuller();
+
+ private:
+  bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 40329b7..d661ee8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -82,6 +82,8 @@
 const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16;
 const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17;
 const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18;
+const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22;
+const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23;
 const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1;
 const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2;
 
@@ -206,6 +208,25 @@
     it->second->broadcast_sent_time_sec.push_back(timeSec);
 }
 
+void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) {
+    noteActiveStatusChanged(key, activated, getWallClockSec());
+}
+
+void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) {
+    lock_guard<std::mutex> lock(mLock);
+    auto it = mConfigStats.find(key);
+    if (it == mConfigStats.end()) {
+        ALOGE("Config key %s not found!", key.ToString().c_str());
+        return;
+    }
+    auto& vec = activated ? it->second->activation_time_sec
+                          : it->second->deactivation_time_sec;
+    if (vec.size() == kMaxTimestampCount) {
+        vec.pop_front();
+    }
+    vec.push_back(timeSec);
+}
+
 void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) {
     noteDataDropped(key, totalBytes, getWallClockSec());
 }
@@ -457,6 +478,11 @@
     getAtomMetricStats(metricId).bucketDropped++;
 }
 
+void StatsdStats::noteBucketUnknownCondition(int64_t metricId) {
+    lock_guard<std::mutex> lock(mLock);
+    getAtomMetricStats(metricId).bucketUnknownCondition++;
+}
+
 void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) {
     lock_guard<std::mutex> lock(mLock);
     getAtomMetricStats(metricId).conditionChangeInNextBucket++;
@@ -476,7 +502,7 @@
             std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
 }
 
-StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) {
+StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
     auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
     if (atomMetricStatsIter != mAtomMetricStats.end()) {
         return atomMetricStatsIter->second;
@@ -501,6 +527,8 @@
     mLogLossStats.clear();
     for (auto& config : mConfigStats) {
         config.second->broadcast_sent_time_sec.clear();
+        config.second->activation_time_sec.clear();
+        config.second->deactivation_time_sec.clear();
         config.second->data_drop_time_sec.clear();
         config.second->data_drop_bytes.clear();
         config.second->dump_report_stats.clear();
@@ -558,6 +586,14 @@
             dprintf(out, "\tbroadcast time: %d\n", broadcastTime);
         }
 
+        for (const int& activationTime : configStats->activation_time_sec) {
+            dprintf(out, "\tactivation time: %d\n", activationTime);
+        }
+
+        for (const int& deactivationTime : configStats->deactivation_time_sec) {
+            dprintf(out, "\tdeactivation time: %d\n", deactivationTime);
+        }
+
         auto dropTimePtr = configStats->data_drop_time_sec.begin();
         auto dropBytesPtr = configStats->data_drop_bytes.begin();
         for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
@@ -586,6 +622,14 @@
                     (long long)broadcastTime);
         }
 
+        for (const int& activationTime : configStats->activation_time_sec) {
+            dprintf(out, "\tactivation time: %d\n", activationTime);
+        }
+
+        for (const int& deactivationTime : configStats->deactivation_time_sec) {
+            dprintf(out, "\tdeactivation time: %d\n", deactivationTime);
+        }
+
         auto dropTimePtr = configStats->data_drop_time_sec.begin();
         auto dropBytesPtr = configStats->data_drop_bytes.begin();
         for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
@@ -696,6 +740,16 @@
                      broadcast);
     }
 
+    for (const auto& activation : configStats.activation_time_sec) {
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED,
+                     activation);
+    }
+
+    for (const auto& deactivation : configStats.deactivation_time_sec) {
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED,
+                     deactivation);
+    }
+
     for (const auto& drop_time : configStats.data_drop_time_sec) {
         proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED,
                      drop_time);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 65e8a32..e039be2 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -42,6 +42,13 @@
     bool is_valid;
 
     std::list<int32_t> broadcast_sent_time_sec;
+
+    // Times at which this config is activated.
+    std::list<int32_t> activation_time_sec;
+
+    // Times at which this config is deactivated.
+    std::list<int32_t> deactivation_time_sec;
+
     std::list<int32_t> data_drop_time_sec;
     // Number of bytes dropped at corresponding time.
     std::list<int64_t> data_drop_bytes;
@@ -132,6 +139,9 @@
     /* Min period between two checks of byte size per config key in nanoseconds. */
     static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
 
+    /* Minimum period between two activation broadcasts in nanoseconds. */
+    static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC;
+
     // Maximum age (30 days) that files on disk can exist in seconds.
     static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
 
@@ -175,6 +185,13 @@
     void noteBroadcastSent(const ConfigKey& key);
 
     /**
+     * Report that a config has become activated or deactivated.
+     * This can be different from whether or not a broadcast is sent if the
+     * guardrail prevented the broadcast from being sent.
+     */
+    void noteActiveStatusChanged(const ConfigKey& key, bool activate);
+
+    /**
      * Report a config's metrics data has been dropped.
      */
     void noteDataDropped(const ConfigKey& key, const size_t totalBytes);
@@ -392,6 +409,11 @@
     void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
 
     /**
+     * Number of buckets with unknown condition.
+     */
+    void noteBucketUnknownCondition(int64_t metricId);
+
+    /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
      * to collect stats after reset() has been called.
@@ -441,6 +463,7 @@
         long bucketDropped = 0;
         int64_t minBucketBoundaryDelayNs = 0;
         int64_t maxBucketBoundaryDelayNs = 0;
+        long bucketUnknownCondition = 0;
     } AtomMetricStats;
 
 private:
@@ -471,7 +494,7 @@
     std::map<int, PulledAtomStats> mPulledAtomStats;
 
     // Maps metric ID to its stats. The size is capped by the number of metrics.
-    std::map<int, AtomMetricStats> mAtomMetricStats;
+    std::map<int64_t, AtomMetricStats> mAtomMetricStats;
 
     struct LogLossStats {
         LogLossStats(int32_t sec, int32_t count, int32_t error)
@@ -507,13 +530,15 @@
 
     void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
 
+    void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec);
+
     void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
 
     /**
      * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
      * will live as long as `this`.
      */
-    StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId);
+    StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
 
     FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 40a4070..dec36b5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,6 +20,8 @@
 #include "stats_log_util.h"
 #include "statslog.h"
 
+#include <binder/IPCThreadState.h>
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -180,6 +182,25 @@
     }
 }
 
+LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
+                   bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+                   const std::vector<uint8_t>& experimentIds, int32_t userId) {
+    mLogdTimestampNs = getWallClockNs();
+    mElapsedTimestampNs = getElapsedRealtimeNs();
+    mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
+    mLogUid = android::IPCThreadState::self()->getCallingUid();
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
+}
+
 LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                    const SpeakerImpedance& speakerImpedance) {
     mLogdTimestampNs = wallClockTimestampNs;
@@ -340,6 +361,16 @@
     }
 }
 
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const InstallTrainInfo& trainInfo) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::TRAIN_INFO;
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds)));
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {}
 
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 784376a..111a619 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -55,6 +55,11 @@
     int32_t mUid;
     std::string mTag;
 };
+
+struct InstallTrainInfo {
+    int64_t trainVersionCode;
+    std::vector<uint8_t> experimentIds;
+};
 /**
  * Wrapper for the log_msg structure.
  */
@@ -97,6 +102,11 @@
                       const std::map<int32_t, std::string>& string_map,
                       const std::map<int32_t, float>& float_map);
 
+    // Constructs a BinaryPushStateChanged LogEvent from API call.
+    explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
+                      bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+                      const std::vector<uint8_t>& experimentIds, int32_t userId);
+
     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                       const SpeakerImpedance& speakerImpedance);
 
@@ -127,6 +137,9 @@
     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                       const VendorAtom& vendorAtom);
 
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const InstallTrainInfo& installTrainInfo);
+
     ~LogEvent();
 
     /**
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 350745b..e84f88d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -250,7 +250,7 @@
 void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
@@ -303,7 +303,7 @@
         if (prev != mCurrentFullCounters->end()) {
             countWholeBucket += prev->second;
         }
-        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
                                          countWholeBucket);
     }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 6c1c47b..da6b97c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -433,7 +433,7 @@
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                       const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
     flushIfNeededLocked(eventTime);
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         for (auto& pair : whatIt.second) {
@@ -767,12 +767,13 @@
                            !mSameConditionDimensionsInTracker,
                            !mHasLinksToAllConditionDimensionsInTracker,
                            &dimensionKeysInCondition);
-        condition = (conditionState == ConditionState::kTrue);
+        condition = conditionState == ConditionState::kTrue;
         if (mDimensionsInCondition.empty() && condition) {
             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
         }
     } else {
-        condition = mCondition;
+        // TODO: The unknown condition state is not handled here, we should fix it.
+        condition = mCondition == ConditionState::kTrue;
         if (condition) {
             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
         }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 1b830a3..ec561b5 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -137,6 +137,7 @@
 
     FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
     FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+    FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
     FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
     FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
     FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 7e695a6..3b4af65 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -132,7 +132,7 @@
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index d56a355..6301793 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -320,15 +320,15 @@
         // When the metric wants to do random sampling and there is already one gauge atom for the
         // current bucket, do not do it again.
         case GaugeMetric::RANDOM_ONE_SAMPLE: {
-            triggerPuller = mCondition && mCurrentSlicedBucket->empty();
+            triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
             break;
         }
         case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
-            triggerPuller = mCondition;
+            triggerPuller = mCondition == ConditionState::kTrue;
             break;
         }
         case GaugeMetric::FIRST_N_SAMPLES: {
-            triggerPuller = mCondition;
+            triggerPuller = mCondition == ConditionState::kTrue;
             break;
         }
         default:
@@ -364,7 +364,7 @@
                                                    const int64_t eventTimeNs) {
     VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
     flushIfNeededLocked(eventTimeNs);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
     if (mIsPulled && mTriggerAtomId == -1) {
         pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
@@ -377,7 +377,7 @@
     flushIfNeededLocked(eventTimeNs);
     // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
     // pull for every dimension.
-    mCondition = overallCondition;
+    mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
     if (mIsPulled && mTriggerAtomId == -1) {
         pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
@@ -485,8 +485,8 @@
                 gaugeVal = value.long_value;
             }
             for (auto& tracker : mAnomalyTrackers) {
-                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
-                                                 gaugeVal);
+                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
+                                                 eventKey, gaugeVal);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 495138e..4cf5333 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,7 +45,8 @@
                            &dimensionKeysInCondition);
         condition = (conditionState == ConditionState::kTrue);
     } else {
-        condition = mCondition;
+        // TODO: The unknown condition state is not handled here, we should fix it.
+        condition = mCondition == ConditionState::kTrue;
     }
 
     if (mDimensionsInCondition.empty() && condition) {
@@ -94,7 +95,7 @@
 void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) {
     std::lock_guard<std::mutex> lock(mMutex);
     // When a metric producer does not depend on any activation, its mIsActive is true.
-    // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
+    // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
     // change.
     if  (mEventActivationMap.empty()) {
         mIsActive = false;
@@ -107,7 +108,8 @@
     if (it == mEventActivationMap.end()) {
         return;
     }
-    if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) {
+    if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT &&
+        it->second.state == ActivationState::kNotActive) {
         it->second.state = ActivationState::kActiveOnBoot;
         return;
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 849cb76..8ab3b06 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -59,7 +59,7 @@
           mTimeBaseNs(timeBaseNs),
           mCurrentBucketStartTimeNs(timeBaseNs),
           mCurrentBucketNum(0),
-          mCondition(conditionIndex >= 0 ? false : true),
+          mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue),
           mConditionSliced(false),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
@@ -315,7 +315,7 @@
 
     int64_t mBucketSizeNs;
 
-    bool mCondition;
+    ConditionState mCondition;
 
     bool mConditionSliced;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6ed6ab50..4851a8d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -118,6 +118,16 @@
         ALOGE("This config has too many alerts! Reject!");
         mConfigValid = false;
     }
+
+    mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
+            (mAllMetricProducers.size() == 0);
+    bool isActive = mIsAlwaysActive;
+    for (int metric : mMetricIndexesWithActivation) {
+        isActive |= mAllMetricProducers[metric]->isActive();
+    }
+    mIsActive = isActive;
+    VLOG("mIsActive is initialized to %d", mIsActive)
+
     // no matter whether this config is valid, log it in the stats.
     StatsdStats::getInstance().noteConfigReceived(
             key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
@@ -332,12 +342,14 @@
     int tagId = event.GetTagId();
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
 
-    bool isActive = false;
+    bool isActive = mIsAlwaysActive;
     for (int metric : mMetricIndexesWithActivation) {
         mAllMetricProducers[metric]->flushIfExpire(eventTimeNs);
         isActive |= mAllMetricProducers[metric]->isActive();
     }
 
+    mIsActive = isActive;
+
     if (mTagIds.find(tagId) == mTagIds.end()) {
         // not interesting...
         return;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index cb1cefb..eab1f76 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -128,6 +128,8 @@
     // Does not change the state.
     virtual size_t byteSize();
 
+    // Returns whether or not this config is active.
+    // The config is active if any metric in the config is active.
     inline bool isActive() const {
         return mIsActive;
     }
@@ -241,9 +243,12 @@
     // The metrics that don't need to be uploaded or even reported.
     std::set<int64_t> mNoReportMetricIds;
 
-    // Any metric active means the config is active.
+   // The config is active if any metric in the config is active.
     bool mIsActive;
 
+    // The config is always active if any metric in the config does not have an activation signal.
+    bool mIsAlwaysActive;
+
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 851ae99..3cf378d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -154,7 +154,7 @@
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
     // Kicks off the puller immediately if condition is true and diff based.
-    if (mIsPulled && mCondition && mUseDiff) {
+    if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
         pullAndMatchEventsLocked(startTimeNs);
     }
     VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
@@ -341,17 +341,21 @@
     flushIfNeededLocked(eventTimeNs);
 
     // Pull on condition changes.
-    if (mIsPulled && (mCondition != condition)) {
+    bool conditionChanged = mCondition != condition;
+    bool unknownToFalse = mCondition == ConditionState::kUnknown
+            && condition == ConditionState::kFalse;
+    // We do not need to pull when we go from unknown to false.
+    if (mIsPulled && conditionChanged && !unknownToFalse) {
         pullAndMatchEventsLocked(eventTimeNs);
     }
 
     // when condition change from true to false, clear diff base but don't
     // reset other counters as we may accumulate more value in the bucket.
-    if (mUseDiff && mCondition && !condition) {
+    if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
         resetBase();
     }
 
-    mCondition = condition;
+    mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
@@ -372,7 +376,7 @@
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
                                        bool pullSuccess, int64_t originalPullTimeNs) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (mCondition) {
+    if (mCondition == ConditionState::kTrue) {
         if (!pullSuccess) {
             // If the pull failed, we won't be able to compute a diff.
             invalidateCurrentBucket();
@@ -693,8 +697,8 @@
             wholeBucketVal += prev->second;
         }
         for (auto& tracker : mAnomalyTrackers) {
-            tracker->detectAndDeclareAnomaly(
-                eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal);
+            tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
+                                             wholeBucketVal);
         }
     }
 }
@@ -725,6 +729,10 @@
 }
 
 void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
+    if (mCondition == ConditionState::kUnknown) {
+        StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+    }
+
     VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
          (int)mCurrentSlicedBucket.size());
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index ccb1d43..081e61e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -155,8 +155,8 @@
                                  const int64_t& currentBucketValue) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
-                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
-                                                        currentBucketValue);
+                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
+                                                        mEventKey, currentBucketValue);
             }
         }
     }
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 5cf012638..d4b57dd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -73,6 +73,11 @@
 
 UidMap::~UidMap() {}
 
+sp<UidMap> UidMap::getInstance() {
+    static sp<UidMap> sInstance = new UidMap();
+    return sInstance;
+}
+
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
@@ -336,6 +341,61 @@
     return mBytesUsed;
 }
 
+void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings,
+                                 bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                 std::set<string>* str_set, ProtoOutputStream* proto) {
+    lock_guard<mutex> lock(mMutex);
+
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids,
+                              str_set, proto);
+}
+
+void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                       bool includeInstaller,
+                                       const std::set<int32_t>& interestingUids,
+                                       std::set<string>* str_set, ProtoOutputStream* proto) {
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
+    for (const auto& kv : mMap) {
+        if (!interestingUids.empty() &&
+            interestingUids.find(kv.first.first) == interestingUids.end()) {
+            continue;
+        }
+        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+        if (str_set != nullptr) {
+            str_set->insert(kv.first.second);
+            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
+                         (long long)Hash64(kv.first.second));
+            if (includeVersionStrings) {
+                str_set->insert(kv.second.versionString);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+                             (long long)Hash64(kv.second.versionString));
+            }
+            if (includeInstaller) {
+                str_set->insert(kv.second.installer);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+                             (long long)Hash64(kv.second.installer));
+            }
+        } else {
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+            if (includeVersionStrings) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+                             kv.second.versionString);
+            }
+            if (includeInstaller) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+                             kv.second.installer);
+            }
+        }
+
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                     (long long)kv.second.versionCode);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
+        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
+        proto->end(token);
+    }
+}
+
 void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
                           bool includeVersionStrings, bool includeInstaller,
                           ProtoOutputStream* proto) {
@@ -381,43 +441,9 @@
     // Write snapshot from current uid map state.
     uint64_t snapshotsToken =
             proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
-    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
-    for (const auto& kv : mMap) {
-        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
-
-        if (str_set != nullptr) {
-            str_set->insert(kv.first.second);
-            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
-                         (long long)Hash64(kv.first.second));
-            if (includeVersionStrings) {
-                str_set->insert(kv.second.versionString);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
-                             (long long)Hash64(kv.second.versionString));
-            }
-            if (includeInstaller) {
-                str_set->insert(kv.second.installer);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
-                             (long long)Hash64(kv.second.installer));
-            }
-        } else {
-            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
-            if (includeVersionStrings) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
-                             kv.second.versionString);
-            }
-            if (includeInstaller) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
-                             kv.second.installer);
-            }
-        }
-
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                     (long long)kv.second.versionCode);
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
-        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
-        proto->end(token);
-    }
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller,
+                              std::set<int32_t>() /*empty uid set means including every uid*/,
+                              str_set, proto);
     proto->end(snapshotsToken);
 
     int64_t prevMin = getMinimumTimestampNs();
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 75ff507..a7c5fb2 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,6 +91,8 @@
     UidMap();
     ~UidMap();
     static const std::map<std::string, uint32_t> sAidToUidMapping;
+
+    static sp<UidMap> getInstance();
     /*
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
@@ -152,12 +154,25 @@
 
     std::set<int32_t> getAppUid(const string& package) const;
 
+    // Write current PackageInfoSnapshot to ProtoOutputStream.
+    // interestingUids: If not empty, only write the package info for these uids. If empty, write
+    //                  package info for all uids.
+    // str_set: if not null, add new string to the set and write str_hash to proto
+    //          if null, write string to proto.
+    void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller,
+                             const std::set<int32_t>& interestingUids, std::set<string>* str_set,
+                             ProtoOutputStream* proto);
+
 private:
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
+    void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                   bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                   std::set<string>* str_set, ProtoOutputStream* proto);
+
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 863261a..623d8bc 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -367,6 +367,8 @@
             optional int32 field_int32 = 2;
         }
         repeated Annotation annotation = 18;
+        repeated int32 activation_time_sec = 22;
+        repeated int32 deactivation_time_sec = 23;
     }
 
     repeated ConfigStats config_stats = 3;
@@ -423,6 +425,7 @@
       optional int64 bucket_dropped = 8;
       optional int64 min_bucket_boundary_delay_ns = 9;
       optional int64 max_bucket_boundary_delay_ns = 10;
+      optional int64 bucket_unknown_condition = 11;
     }
     repeated AtomMetricStats atom_metric_stats = 17;
 
@@ -454,3 +457,17 @@
     }
     repeated LogLossStats detected_log_loss = 16;
 }
+
+message AlertTriggerDetails {
+    message MetricValue {
+        optional int64 metric_id = 1;
+        optional DimensionsValue dimension_in_what = 2;
+        optional DimensionsValue dimension_in_condition = 3;
+        optional int64 value = 4;
+    }
+    oneof value {
+        MetricValue trigger_metric = 1;
+        EventMetricData trigger_event = 2;
+    }
+    optional UidMapping.PackageInfoSnapshot package_info = 3;
+}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 0ebf2ca..ca645e1 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -84,6 +84,7 @@
 const int FIELD_ID_BUCKET_DROPPED = 8;
 const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9;
 const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
+const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
 
 namespace {
 
@@ -489,11 +490,11 @@
     protoOutput->end(token);
 }
 
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
                                   util::ProtoOutputStream *protoOutput) {
     uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
                                         FIELD_COUNT_REPEATED);
-    protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, (int32_t)pair.first);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
                        (long long)pair.second.hardDimensionLimitReached);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
@@ -512,6 +513,8 @@
                        (long long)pair.second.minBucketBoundaryDelayNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
                        (long long)pair.second.maxBucketBoundaryDelayNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
+                       (long long)pair.second.bucketUnknownCondition);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index dcea0e6..59d4865 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -74,7 +74,7 @@
                               util::ProtoOutputStream* protoOutput);
 
 // Helper function to write AtomMetricStats to ProtoOutputStream
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
                                   util::ProtoOutputStream *protoOutput);
 
 template<class T>
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 90f641a..165e57c 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -38,10 +38,13 @@
 
 #define STATS_DATA_DIR "/data/misc/stats-data"
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
+#define TRAIN_INFO_DIR "/data/misc/train-info"
 
 // for ConfigMetricsReportList
 const int FIELD_ID_REPORTS = 2;
 
+std::mutex StorageManager::sTrainInfoMutex;
+
 using android::base::StringPrintf;
 using std::unique_ptr;
 
@@ -92,6 +95,71 @@
     close(fd);
 }
 
+bool StorageManager::writeTrainInfo(int64_t trainVersionCode,
+                                    const std::vector<uint8_t>& experimentIds) {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+    deleteAllFiles(TRAIN_INFO_DIR);
+
+    string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);
+
+    int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    if (fd == -1) {
+        VLOG("Attempt to access %s but failed", file_name.c_str());
+        return false;
+    }
+
+    size_t result = write(fd, experimentIds.data(), experimentIds.size());
+    if (result == experimentIds.size()) {
+        VLOG("Successfully wrote %s", file_name.c_str());
+    } else {
+        VLOG("Failed to write %s", file_name.c_str());
+        return false;
+    }
+
+    result = fchown(fd, AID_STATSD, AID_STATSD);
+    if (result) {
+        VLOG("Failed to chown %s to statsd", file_name.c_str());
+        return false;
+    }
+
+    close(fd);
+    return true;
+}
+
+bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+
+    if (dir == NULL) {
+        VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+        return false;
+    }
+
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* name = de->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+        trainInfo.trainVersionCode = StrToInt64(name);
+        string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
+        int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
+        if (fd != -1) {
+            string str;
+            if (android::base::ReadFdToString(fd, &str)) {
+                close(fd);
+                std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds));
+                VLOG("Read train info file successful: %s", fullPath.c_str());
+                return true;
+            }
+        }
+        close(fd);
+    }
+    return false;
+}
+
 void StorageManager::deleteFile(const char* file) {
     if (remove(file) != 0) {
         VLOG("Attempt to delete %s but is not found", file);
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index dcf3bb6..d6df867 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -29,6 +29,11 @@
 
 using android::util::ProtoOutputStream;
 
+struct TrainInfo {
+    int64_t trainVersionCode;
+    std::vector<uint8_t> experimentIds;
+};
+
 class StorageManager : public virtual RefBase {
 public:
     /**
@@ -37,6 +42,16 @@
     static void writeFile(const char* file, const void* buffer, int numBytes);
 
     /**
+     * Writes train info.
+     */
+    static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds);
+
+    /**
+     * Reads train info.
+     */
+    static bool readTrainInfo(InstallTrainInfo& trainInfo);
+
+    /**
      * Reads the file content to the buffer.
      */
     static bool readFileToString(const char* file, string* content);
@@ -109,6 +124,8 @@
      * Prints disk usage statistics about a directory related to statsd.
      */
     static void printDirStats(int out, const char* path);
+
+    static std::mutex sTrainInfoMutex;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index 42cac0c..0ed2d75 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -16,7 +16,10 @@
 #define DEBUG false
 #include "Log.h"
 
+#include "FieldValue.h"
 #include "IncidentdReporter.h"
+#include "packages/UidMap.h"
+#include "stats_log_util.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -43,8 +46,18 @@
 const int FIELD_ID_CONFIG_KEY_UID = 1;
 const int FIELD_ID_CONFIG_KEY_ID = 2;
 
+const int FIELD_ID_TRIGGER_DETAILS = 4;
+const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
+const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
+const int FIELD_ID_METRIC_VALUE_VALUE = 4;
+
+const int FIELD_ID_PACKAGE_INFO = 3;
+
 namespace {
-void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                  int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
     ProtoOutputStream headerProto;
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
     uint64_t token =
@@ -53,6 +66,58 @@
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
     headerProto.end(token);
 
+    token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
+
+    // MetricValue trigger_metric = 1;
+    uint64_t metricToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
+    // message MetricValue {
+    // optional int64 metric_id = 1;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
+    // optional DimensionsValue dimension_in_what = 2;
+    uint64_t dimToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional DimensionsValue dimension_in_condition = 3;
+    dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional int64 value = 4;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
+
+    // }
+    headerProto.end(metricToken);
+
+    // write relevant uid package info
+    std::set<int32_t> uids;
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
+        int uid = getUidIfExists(dim);
+        // any uid <= 2000 are predefined AID_*
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
+        int uid = getUidIfExists(dim);
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    if (!uids.empty()) {
+        uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
+        UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
+                                                   nullptr /*string set*/, &headerProto);
+        headerProto.end(token);
+    }
+
+    headerProto.end(token);
+
     protoData->resize(headerProto.size());
     size_t pos = 0;
     auto iter = headerProto.data();
@@ -65,7 +130,8 @@
 }
 }  // namespace
 
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey) {
     if (config.section_size() == 0) {
         VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
@@ -76,7 +142,7 @@
     IncidentReportArgs incidentReport;
 
     vector<uint8_t> protoData;
-    getProtoData(rule_id, configKey, &protoData);
+    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
     incidentReport.addHeader(protoData);
 
     for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 1b83fe2..e78a4d9 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HashableDimensionKey.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert, IncidentdDetails
 
@@ -26,7 +27,8 @@
 /**
  * Calls incidentd to trigger an incident report and put in dropbox for uploading.
  */
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey);
 
 }  // namespace statsd
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index e0cbd5d..a98ecd5 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -27,3 +27,4 @@
     mkdir /data/misc/stats-data/ 0770 statsd system
     mkdir /data/misc/stats-service/ 0770 statsd system
     mkdir /data/misc/stats-active-metric/ 0770 statsd system
+    mkdir /data/misc/train-info/ 0770 statsd system
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 60df165..4579ca6 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -65,7 +65,8 @@
     sp<AlarmMonitor> periodicAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
     StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
-                        [](const ConfigKey& key) { return true; });
+                        [](const ConfigKey& key) { return true; },
+                        [](const int&, const vector<int64_t>&) {return true;});
 
     MockMetricsManager mockMetricsManager;
 
@@ -88,7 +89,8 @@
                         [&broadcastCount](const ConfigKey& key) {
                             broadcastCount++;
                             return true;
-                        });
+                        },
+                        [](const int&, const vector<int64_t>&) {return true;});
 
     MockMetricsManager mockMetricsManager;
 
@@ -118,7 +120,8 @@
                         [&broadcastCount](const ConfigKey& key) {
                             broadcastCount++;
                             return true;
-                        });
+                        },
+                        [](const int&, const vector<int64_t>&) {return true;});
 
     MockMetricsManager mockMetricsManager;
 
@@ -162,7 +165,8 @@
                         [&broadcastCount](const ConfigKey& key) {
                             broadcastCount++;
                             return true;
-                        });
+                        },
+                        [](const int&, const vector<int64_t>&) {return true;});
     ConfigKey key(3, 4);
     StatsdConfig config = MakeConfig(true);
     p.OnConfigUpdated(0, key, config);
@@ -192,7 +196,8 @@
                         [&broadcastCount](const ConfigKey& key) {
                             broadcastCount++;
                             return true;
-                        });
+                        },
+                        [](const int&, const vector<int64_t>&) {return true;});
     ConfigKey key(3, 4);
     StatsdConfig config = MakeConfig(false);
     p.OnConfigUpdated(0, key, config);
@@ -218,7 +223,8 @@
                         [&broadcastCount](const ConfigKey& key) {
                             broadcastCount++;
                             return true;
-                        });
+                        },
+                        [](const int&, const vector<int64_t>&) {return true;});
     ConfigKey key(3, 4);
     StatsdConfig config;
     auto annotation = config.add_annotation();
@@ -291,7 +297,8 @@
 
     // Setup a simple config, no activation
     StatsdConfig config1;
-    config1.set_id(12341);
+    int64_t cfgId1 = 12341;
+    config1.set_id(cfgId1);
     config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
     *config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -308,14 +315,12 @@
     countMetric2->set_what(wakelockAcquireMatcher.id());
     countMetric2->set_bucket(FIVE_MINUTES);
 
-    ConfigKey cfgKey1(uid, 12341);
-    long timeBase1 = 1;
-    sp<StatsLogProcessor> processor =
-            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+    ConfigKey cfgKey1(uid, cfgId1);
 
     // Add another config, with two metrics, one with activation
     StatsdConfig config2;
-    config2.set_id(12342);
+    int64_t cfgId2 = 12342;
+    config2.set_id(cfgId2);
     config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     *config2.add_atom_matcher() = wakelockAcquireMatcher;
 
@@ -338,11 +343,12 @@
     metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
     metric3ActivationTrigger->set_ttl_seconds(100);
 
-    ConfigKey cfgKey2(uid, 12342);
+    ConfigKey cfgKey2(uid, cfgId2);
 
     // Add another config, with two metrics, both with activations
     StatsdConfig config3;
-    config3.set_id(12342);
+    int64_t cfgId3 = 12343;
+    config3.set_id(cfgId3);
     config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     *config3.add_atom_matcher() = wakelockAcquireMatcher;
 
@@ -370,14 +376,37 @@
     metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
     metric6ActivationTrigger->set_ttl_seconds(200);
 
-    ConfigKey cfgKey3(uid, 12343);
+    ConfigKey cfgKey3(uid, cfgId3);
 
-    processor->OnConfigUpdated(2, cfgKey2, config2);
-    processor->OnConfigUpdated(3, cfgKey3, config3);
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
 
-    EXPECT_EQ(3, processor->mMetricsManagers.size());
-    auto it = processor->mMetricsManagers.find(cfgKey1);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+            timeBase1, [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                    const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+                        activeConfigs.begin(), activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(1, cfgKey1, config1);
+    processor.OnConfigUpdated(2, cfgKey2, config2);
+    processor.OnConfigUpdated(3, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor.mMetricsManagers.size());
+
+    // Expect the first config and both metrics in it to be active.
+    auto it = processor.mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager1 = it->second;
     EXPECT_TRUE(metricsManager1->isActive());
 
@@ -401,8 +430,9 @@
     auto& metricProducer2 = *metricIt;
     EXPECT_TRUE(metricProducer2->isActive());
 
-    it = processor->mMetricsManagers.find(cfgKey2);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
+    it = processor.mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager2 = it->second;
     EXPECT_TRUE(metricsManager2->isActive());
 
@@ -426,8 +456,9 @@
     auto& metricProducer4 = *metricIt;
     EXPECT_TRUE(metricProducer4->isActive());
 
-    it = processor->mMetricsManagers.find(cfgKey3);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    // Expect the third config and both metrics in it to be inactive.
+    it = processor.mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager3 = it->second;
     EXPECT_FALSE(metricsManager3->isActive());
 
@@ -451,10 +482,30 @@
     auto& metricProducer6 = *metricIt;
     EXPECT_FALSE(metricProducer6->isActive());
 
+    // No broadcast for active configs should have happened yet.
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activate all 3 metrics that were not active.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
     auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
+    // Assert that all 3 configs are active.
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
+            != activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
+            != activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
+            != activeConfigsBroadcast.end());
+
+    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
     int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
     EXPECT_TRUE(metricProducer3->isActive());
     int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime);
@@ -466,8 +517,9 @@
     int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime);
     EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6);
 
-    processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC);
+    processor.WriteMetricsActivationToDisk(shutDownTime);
 
+    // Create a second StatsLogProcessor and push the same 3 configs.
     long timeBase2 = 1000;
     sp<StatsLogProcessor> processor2 =
             CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
@@ -475,6 +527,8 @@
     processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
 
     EXPECT_EQ(3, processor2->mMetricsManagers.size());
+
+    // First config and both metrics are active.
     it = processor2->mMetricsManagers.find(cfgKey1);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1001 = it->second;
@@ -500,6 +554,7 @@
     auto& metricProducer1002 = *metricIt;
     EXPECT_TRUE(metricProducer1002->isActive());
 
+    // Second config is active. Metric 3 is inactive, metric 4 is active.
     it = processor2->mMetricsManagers.find(cfgKey2);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1002 = it->second;
@@ -525,6 +580,7 @@
     auto& metricProducer1004 = *metricIt;
     EXPECT_TRUE(metricProducer1004->isActive());
 
+    // Config 3 is inactive. both metrics are inactive.
     it = processor2->mMetricsManagers.find(cfgKey3);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1003 = it->second;
@@ -551,6 +607,7 @@
     auto& metricProducer1006 = *metricIt;
     EXPECT_FALSE(metricProducer1006->isActive());
 
+    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
     EXPECT_FALSE(metricProducer1003->isActive());
     const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
     EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns);
@@ -566,12 +623,16 @@
 
     processor2->LoadMetricsActivationFromDisk();
 
+    // After loading activations from disk, assert that all 3 metrics are active.
     EXPECT_TRUE(metricProducer1003->isActive());
     EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns);
     EXPECT_TRUE(metricProducer1005->isActive());
     EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns);
     EXPECT_TRUE(metricProducer1006->isActive());
     EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns);
+
+    // Make sure no more broadcasts have happened.
+    EXPECT_EQ(broadcastCount, 1);
 }
 
 TEST(StatsLogProcessorTest, TestActivationOnBoot) {
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index f0d9cf1..c04a40c 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -45,7 +45,8 @@
     sp<AlarmMonitor> subscriberAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
     StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [](const ConfigKey& key) { return true; });
+                        [](const ConfigKey& key) { return true; },
+                        [](const int&, const vector<int64_t>&) {return true;});
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 960fbda..c10703c 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -90,7 +90,8 @@
                                const std::shared_ptr<DimToValMap>& bucket,
                                const int64_t& eventTimestamp) {
     for (const auto& kv : *bucket) {
-        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second);
+        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
+                                        kv.second);
     }
 }
 
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 29e86f3..85d8a56 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -66,17 +66,42 @@
     auto config = CreateStatsdConfig();
 
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs =
-        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
-    ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    sp<MetricProducer> metricProducer =
-        processor->mMetricsManagers.begin()->second->mAllMetricProducers[0];
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
+
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+            bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                    const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+                        activeConfigs.begin(), activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
     auto& eventActivationMap = metricProducer->mEventActivationMap;
 
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
     // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
     // triggered by screen on event (tracker index 2).
@@ -93,13 +118,19 @@
     std::unique_ptr<LogEvent> event;
 
     event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
 
     // Activated by battery save mode.
     event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -109,12 +140,13 @@
 
     // First processed event.
     event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     // Activated by screen on event.
     event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
                                           bucketStartTimeNs + 20);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -126,7 +158,8 @@
     // 2nd processed event.
     // The activation by screen_on event expires, but the one by battery save mode is still active.
     event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -134,15 +167,21 @@
     EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
     EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
 
     // 3rd processed event.
     event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     // All activations expired.
     event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -153,8 +192,12 @@
     // Re-activate.
     event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
                                           bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -163,11 +206,11 @@
     EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
 
     event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
                             ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 44a88f0..1ff7982 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -133,6 +133,13 @@
     stats.noteMetricsReportSent(key, 0);
     stats.noteMetricsReportSent(key, 0);
 
+    // activation_time_sec -> 2
+    stats.noteActiveStatusChanged(key, true);
+    stats.noteActiveStatusChanged(key, true);
+
+    // deactivation_time_sec -> 1
+    stats.noteActiveStatusChanged(key, false);
+
     vector<uint8_t> output;
     stats.dumpStats(&output, true);  // Dump and reset stats
     StatsdStatsReport report;
@@ -146,6 +153,8 @@
     EXPECT_EQ(123, configReport.data_drop_bytes(0));
     EXPECT_EQ(3, configReport.dump_report_time_sec_size());
     EXPECT_EQ(3, configReport.dump_report_data_size_size());
+    EXPECT_EQ(2, configReport.activation_time_sec_size());
+    EXPECT_EQ(1, configReport.deactivation_time_sec_size());
     EXPECT_EQ(1, configReport.annotation_size());
     EXPECT_EQ(123, configReport.annotation(0).field_int64());
     EXPECT_EQ(456, configReport.annotation(0).field_int32());
@@ -344,6 +353,8 @@
         stats.noteDataDropped(key, timestamps[i]);
         stats.noteBroadcastSent(key, timestamps[i]);
         stats.noteMetricsReportSent(key, 0, timestamps[i]);
+        stats.noteActiveStatusChanged(key, true, timestamps[i]);
+        stats.noteActiveStatusChanged(key, false, timestamps[i]);
     }
 
     int32_t newTimestamp = 10000;
@@ -352,6 +363,8 @@
     stats.noteDataDropped(key, 123, 10000);
     stats.noteBroadcastSent(key, 10000);
     stats.noteMetricsReportSent(key, 0, 10000);
+    stats.noteActiveStatusChanged(key, true, 10000);
+    stats.noteActiveStatusChanged(key, false, 10000);
 
     EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
     const auto& configStats = stats.mConfigStats[key];
@@ -360,17 +373,23 @@
     EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
     EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
     EXPECT_EQ(maxCount, configStats->dump_report_stats.size());
+    EXPECT_EQ(maxCount, configStats->activation_time_sec.size());
+    EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size());
 
     // the oldest timestamp is the second timestamp in history
     EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
-    EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
-    EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
+    EXPECT_EQ(1, configStats->data_drop_bytes.front());
+    EXPECT_EQ(1, configStats->dump_report_stats.front().first);
+    EXPECT_EQ(1, configStats->activation_time_sec.front());
+    EXPECT_EQ(1, configStats->deactivation_time_sec.front());
 
     // the last timestamp is the newest timestamp.
     EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
     EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
     EXPECT_EQ(123, configStats->data_drop_bytes.back());
     EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
+    EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back());
+    EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back());
 }
 
 TEST(StatsdStatsTest, TestSystemServerCrash) {
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index b540964..b294cad 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -117,6 +117,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    durationProducer.mCondition = ConditionState::kFalse;
 
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -140,6 +141,51 @@
     EXPECT_EQ(1LL, buckets2[0].mDuration);
 }
 
+TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    int tagId = 1;
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
+    event1.init();
+    LogEvent event2(tagId, bucketStartTimeNs + 2);
+    event2.init();
+    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
+    event3.init();
+    LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
+    event4.init();
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
+    EXPECT_FALSE(durationProducer.isConditionSliced());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
+    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets2.size());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
+    EXPECT_EQ(1LL, buckets2[0].mDuration);
+}
+
 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
     /**
      * The duration starts from the first bucket, through the two partial buckets (10-70sec),
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 572b199..7e7ffed 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -2176,7 +2176,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     vector<shared_ptr<LogEvent>> allData;
     valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
@@ -2226,7 +2226,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
 
     // Max delay is set to 0 so pull will exceed max delay.
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2257,7 +2257,7 @@
                                       eventMatcherWizard, tagId, bucket2StartTimeNs,
                                       bucket2StartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
 
     // Event should be skipped since it is from previous bucket.
     // Pull should not be called.
@@ -2299,7 +2299,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
     valueProducer.mHasGlobalBase = false;
 
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2351,7 +2351,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2429,7 +2429,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
     valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2);
     EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid);
     EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
@@ -2481,7 +2481,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2564,7 +2564,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index b8b1a1d..2c4f3c7 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -461,7 +461,8 @@
                 [](const sp<IStatsCompanionService>&){});
     sp<StatsLogProcessor> processor =
             new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseNs, [](const ConfigKey&) { return true; });
+                                  timeBaseNs, [](const ConfigKey&) { return true; },
+                                  [](const int&, const vector<int64_t>&) {return true;});
     processor->OnConfigUpdated(currentTimeNs, key, config);
     return processor;
 }
@@ -826,4 +827,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
new file mode 100644
index 0000000..f66de05
--- /dev/null
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include "src/storage/StorageManager.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+
+TEST(StorageManagerTest, TrainInfoReadWriteTest) {
+    InstallTrainInfo trainInfo;
+    trainInfo.trainVersionCode = 12345;
+    const char* expIds = "test_ids";
+    trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
+
+    StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds);
+
+    InstallTrainInfo result;
+    StorageManager::readTrainInfo(result);
+    EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode);
+    EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size());
+    EXPECT_EQ(trainInfo.experimentIds, result.experimentIds);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index d29e68e..3180b77 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -26,6 +26,8 @@
 import android.os.SystemProperties;
 
 public class PowerCommand extends Svc.Command {
+    private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0;
+
     public PowerCommand() {
         super("power");
     }
@@ -42,7 +44,17 @@
                 + "       svc power reboot [reason]\n"
                 + "         Perform a runtime shutdown and reboot device with specified reason.\n"
                 + "       svc power shutdown\n"
-                + "         Perform a runtime shutdown and power off the device.\n";
+                + "         Perform a runtime shutdown and power off the device.\n"
+                + "       svc power forcesuspend [t]\n"
+                + "         Force the system into suspend, ignoring all wakelocks.\n"
+                + "         t - Number of milliseconds to wait before issuing force-suspend.\n"
+                + "             Helps with devices that can't suspend while plugged in.\n"
+                + "             Defaults to " + FORCE_SUSPEND_DELAY_DEFAULT_MILLIS + ".\n"
+                + "             When using a delay, you must use the nohup shell modifier:\n"
+                + "             'adb shell nohup svc power forcesuspend [time]'\n"
+                + "         Use caution; this is dangerous. It puts the device to sleep\n"
+                + "         immediately without giving apps or the system an opportunity to\n"
+                + "         save their state.\n";
     }
 
     public void run(String[] args) {
@@ -101,6 +113,20 @@
                         maybeLogRemoteException("Failed to shutdown.");
                     }
                     return;
+                } else if ("forcesuspend".equals(args[1])) {
+                    int delayMillis = args.length > 2
+                            ? Integer.parseInt(args[2]) : FORCE_SUSPEND_DELAY_DEFAULT_MILLIS;
+                    try {
+                        Thread.sleep(delayMillis);
+                        if (!pm.forceSuspend()) {
+                            System.err.println("Failed to force suspend.");
+                        }
+                    } catch (InterruptedException e) {
+                        System.err.println("Failed to force suspend: " + e);
+                    } catch (RemoteException e) {
+                        maybeLogRemoteException("Failed to force-suspend with exception: " + e);
+                    }
+                    return;
                 }
             }
         }
diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt
index dca3b52..b328f2a 100644
--- a/config/hiddenapi-force-blacklist.txt
+++ b/config/hiddenapi-force-blacklist.txt
@@ -1,4 +1,6 @@
 Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
+Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
+Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V
 Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;
 Ljava/lang/invoke/VarHandle;->acquireFence()V
 Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;
diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-greylist-max-o.txt
index 4b6cc0e..d9c1cd0 100644
--- a/config/hiddenapi-greylist-max-o.txt
+++ b/config/hiddenapi-greylist-max-o.txt
@@ -109777,7 +109777,6 @@
 Ldalvik/system/VMRuntime;->setNonSdkApiUsageConsumer(Ljava/util/function/Consumer;)V
 Ldalvik/system/VMRuntime;->setProcessPackageName(Ljava/lang/String;)V
 Ldalvik/system/VMRuntime;->setSystemDaemonThreadPriority()V
-Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V
 Ldalvik/system/VMRuntime;->startHeapTaskProcessor()V
 Ldalvik/system/VMRuntime;->startJitCompilation()V
 Ldalvik/system/VMRuntime;->stopHeapTaskProcessor()V
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index b72ce89..a836e8e 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -170,7 +170,6 @@
 Landroid/app/IAssistDataReceiver$Stub;-><init>()V
 Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
 Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
-Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
 Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
 Landroid/app/IInstrumentationWatcher;->instrumentationStatus(Landroid/content/ComponentName;ILandroid/os/Bundle;)V
 Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -542,8 +541,6 @@
 Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
 Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
 Landroid/net/INetworkPolicyListener$Stub;-><init>()V
 Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
@@ -2825,7 +2822,6 @@
 Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
 Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V
-Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection;
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone;
diff --git a/core/java/android/annotation/CurrentTimeMillisLong.java b/core/java/android/annotation/CurrentTimeMillisLong.java
index 9846cce..355bb5a 100644
--- a/core/java/android/annotation/CurrentTimeMillisLong.java
+++ b/core/java/android/annotation/CurrentTimeMillisLong.java
@@ -25,12 +25,12 @@
 import java.lang.annotation.Target;
 
 /**
- * @memberDoc Value is a non-negative timestamp in the
- *            {@link System#currentTimeMillis()} time base.
- * @paramDoc Value is a non-negative timestamp in the
- *           {@link System#currentTimeMillis()} time base.
- * @returnDoc Value is a non-negative timestamp in the
- *            {@link System#currentTimeMillis()} time base.
+ * @memberDoc Value is a non-negative timestamp measured as the number of
+ *            milliseconds since 1970-01-01T00:00:00Z.
+ * @paramDoc Value is a non-negative timestamp measured as the number of
+ *            milliseconds since 1970-01-01T00:00:00Z.
+ * @returnDoc Value is a non-negative timestamp measured as the number of
+ *            milliseconds since 1970-01-01T00:00:00Z.
  * @hide
  */
 @Retention(SOURCE)
diff --git a/core/java/android/annotation/CurrentTimeSecondsLong.java b/core/java/android/annotation/CurrentTimeSecondsLong.java
new file mode 100644
index 0000000..2b4ffd7
--- /dev/null
+++ b/core/java/android/annotation/CurrentTimeSecondsLong.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc Value is a non-negative timestamp measured as the number of
+ *            seconds since 1970-01-01T00:00:00Z.
+ * @paramDoc Value is a non-negative timestamp measured as the number of
+ *            seconds since 1970-01-01T00:00:00Z.
+ * @returnDoc Value is a non-negative timestamp measured as the number of
+ *            seconds since 1970-01-01T00:00:00Z.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface CurrentTimeSecondsLong {
+}
diff --git a/core/java/android/annotation/Px.java b/core/java/android/annotation/Px.java
index ad99fdb..cec7f80 100644
--- a/core/java/android/annotation/Px.java
+++ b/core/java/android/annotation/Px.java
@@ -29,6 +29,7 @@
  * Denotes that a numeric parameter, field or method return value is expected
  * to represent a pixel dimension.
  *
+ * @memberDoc This units of this value are pixels.
  * @paramDoc This units of this value are pixels.
  * @returnDoc This units of this value are pixels.
  * {@hide}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0eadd1d..d29fedd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -65,11 +65,13 @@
 import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.GraphicsEnvironment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.StrictMode;
@@ -2296,7 +2298,7 @@
     public final void requestShowKeyboardShortcuts() {
         Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
         intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
-        sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
     /**
@@ -2305,7 +2307,7 @@
     public final void dismissKeyboardShortcutsHelper() {
         Intent intent = new Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS);
         intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
-        sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
     @Override
@@ -3781,14 +3783,14 @@
 
 
     /**
-     * Moves the activity from {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing mode to
-     * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+     * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing mode
+     * and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
      *
      * @hide
      */
     @Override
-    public void exitFreeformMode() throws RemoteException {
-        ActivityTaskManager.getService().exitFreeformMode(mToken);
+    public void toggleFreeformWindowingMode() throws RemoteException {
+        ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken);
     }
 
     /**
@@ -7708,6 +7710,8 @@
             }
         }
 
+        GraphicsEnvironment.getInstance().showAngleInUseDialogBox(this);
+
         mActivityTransitionState.enterReady(this);
         dispatchActivityPostStarted();
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index db3b720..5d4f988 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -55,6 +55,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -68,6 +69,7 @@
 import android.util.Singleton;
 import android.util.Size;
 
+import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.TransferPipe;
@@ -84,7 +86,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * <p>
@@ -2019,8 +2023,9 @@
             return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent,
                     intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Failure from system", e);
+            e.rethrowFromSystemServer();
         }
+        return false;
     }
 
     /**
@@ -3450,6 +3455,35 @@
     }
 
     /**
+     * Sets the current locales of the device. Calling app must have the permission
+     * {@code android.permission.CHANGE_CONFIGURATION} and
+     * {@code android.permission.WRITE_SETTINGS}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setDeviceLocales(@NonNull LocaleList locales) {
+        LocalePicker.updateLocales(locales);
+    }
+
+    /**
+     * Returns a list of supported locales by this system. It includes all locales that are
+     * selectable by the user, potentially including locales that the framework does not have
+     * translated resources for. To get locales that the framework has translated resources for, use
+     * {@code Resources.getSystem().getAssets().getLocales()} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull Collection<Locale> getSupportedLocales() {
+        ArrayList<Locale> locales = new ArrayList<>();
+        for (String localeTag : LocalePicker.getSupportedLocales(mContext)) {
+            locales.add(Locale.forLanguageTag(localeTag));
+        }
+        return locales;
+    }
+
+    /**
      * Get the device configuration attributes.
      */
     public ConfigurationInfo getDeviceConfigurationInfo() {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 69c450c..7d828d8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -259,10 +259,11 @@
 
     public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
             long duration, String tag);
-    public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent,
-            String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
-            Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-            boolean sticky, int userId, boolean allowBackgroundActivityStarts);
+    public abstract int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
+            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
+            int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
+            Bundle bOptions, boolean serialized, boolean sticky, int userId,
+            boolean allowBackgroundActivityStarts);
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage, int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cc419b8..7908637 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3229,8 +3229,9 @@
             TAG, "Handling launch of " + r);
 
         // Initialize before creating the activity
-        if (!ThreadedRenderer.sRendererDisabled) {
-            GraphicsEnvironment.earlyInitEGL();
+        if (!ThreadedRenderer.sRendererDisabled
+                && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
+            HardwareRenderer.preload();
         }
         WindowManagerGlobal.initialize();
 
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index ce71998..e0ae4e3 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -29,12 +29,17 @@
 import android.graphics.Insets;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
+import android.hardware.input.InputManager;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
@@ -291,6 +296,9 @@
 
     /** Send current location and size to the WM to set tap exclude region for this view. */
     private void updateLocation() {
+        if (!isAttachedToWindow()) {
+            return;
+        }
         try {
             getLocationInWindow(mLocationInWindow);
             WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
@@ -343,6 +351,32 @@
         mSurfaceView.setVisibility(visibility);
     }
 
+    /**
+     * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
+     * virtual display.
+     */
+    public void performBackPress() {
+        if (mVirtualDisplay == null) {
+            return;
+        }
+        final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+        final InputManager im = InputManager.getInstance();
+        im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId),
+                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+        im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId),
+                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+    }
+
+    private static KeyEvent createKeyEvent(int action, int code, int displayId) {
+        long when = SystemClock.uptimeMillis();
+        final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
+                0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+                InputDevice.SOURCE_KEYBOARD);
+        ev.setDisplayId(displayId);
+        return ev;
+    }
+
     private void initVirtualDisplay(SurfaceSession surfaceSession) {
         if (mVirtualDisplay != null) {
             throw new IllegalStateException("Trying to initialize for the second time.");
@@ -429,6 +463,9 @@
 
     /** Report to server that tap exclude region on hosting display should be cleared. */
     private void cleanTapExcludeRegion() {
+        if (!isAttachedToWindow()) {
+            return;
+        }
         // Update tap exclude region with an empty rect to clean the state on server.
         try {
             WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 64b94a9..040ad06 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -46,6 +46,8 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+
+import com.android.internal.annotations.Immutable;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
@@ -848,6 +850,7 @@
     /** @hide Has a legacy (non-isolated) view of storage. */
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
     /** @hide Interact with accessibility. */
+    @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
 
     // Warning: If an permission is added here it also has to be added to
@@ -2209,6 +2212,115 @@
     }
 
     /**
+     * Request for getting historical app op usage. The request acts
+     * as a filtering criteria when querying historical op usage.
+     *
+     * @hide
+     */
+    @Immutable
+    @TestApi
+    @SystemApi
+    public static final class HistoricalOpsRequest {
+        private final int mUid;
+        private final @Nullable String mPackageName;
+        private final @Nullable List<String> mOpNames;
+        private final long mBeginTimeMillis;
+        private final long mEndTimeMillis;
+
+        private HistoricalOpsRequest(int uid, @Nullable String packageName,
+                @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis) {
+            mUid = uid;
+            mPackageName = packageName;
+            mOpNames = opNames;
+            mBeginTimeMillis = beginTimeMillis;
+            mEndTimeMillis = endTimeMillis;
+        }
+
+        /**
+         * Builder for creating a {@link HistoricalOpsRequest}.
+         *
+         * @hide
+         */
+        @TestApi
+        @SystemApi
+        public static final class Builder {
+            private int mUid = Process.INVALID_UID;
+            private @Nullable String mPackageName;
+            private @Nullable List<String> mOpNames;
+            private final long mBeginTimeMillis;
+            private final long mEndTimeMillis;
+
+            /**
+             * Creates a new builder.
+             *
+             * @param beginTimeMillis The beginning of the interval in milliseconds since
+             *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
+             *     negative.
+             * @param endTimeMillis The end of the interval in milliseconds since
+             *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
+             *     {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
+             *     history including ops that happen while this call is in flight.
+             */
+            public Builder(long beginTimeMillis, long endTimeMillis) {
+                Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
+                        "beginTimeMillis must be non negative and lesser than endTimeMillis");
+                mBeginTimeMillis = beginTimeMillis;
+                mEndTimeMillis = endTimeMillis;
+            }
+
+            /**
+             * Sets the UID to query for.
+             *
+             * @param uid The uid. Pass {@link android.os.Process#INVALID_UID} for any uid.
+             * @return This builder.
+             */
+            public @NonNull Builder setUid(int uid) {
+                Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
+                        "uid must be " + Process.INVALID_UID + " or non negative");
+                mUid = uid;
+                return this;
+            }
+
+            /**
+             * Sets the package to query for.
+             *
+             * @param packageName The package name. <code>Null</code> for any package.
+             * @return This builder.
+             */
+            public @NonNull Builder setPackageName(@Nullable String packageName) {
+                mPackageName = packageName;
+                return this;
+            }
+
+            /**
+             * Sets the op names to query for.
+             *
+             * @param opNames The op names. <code>Null</code> for any op.
+             * @return This builder.
+             */
+            public @NonNull Builder setOpNames(@Nullable List<String> opNames) {
+                if (opNames != null) {
+                    final int opCount = opNames.size();
+                    for (int i = 0; i < opCount; i++) {
+                        Preconditions.checkArgument(AppOpsManager.strOpToOp(
+                                opNames.get(i)) != AppOpsManager.OP_NONE);
+                    }
+                }
+                mOpNames = opNames;
+                return this;
+            }
+
+            /**
+             * @return a new {@link HistoricalOpsRequest}.
+             */
+            public @NonNull HistoricalOpsRequest build() {
+                return new HistoricalOpsRequest(mUid, mPackageName, mOpNames,
+                        mBeginTimeMillis, mEndTimeMillis);
+            }
+        }
+    }
+
+    /**
      * This class represents historical app op state of all UIDs for a given time interval.
      *
      * @hide
@@ -3670,26 +3782,7 @@
     /**
      * Retrieve historical app op stats for a period.
      *
-     * <p>Historical data can be obtained
-     * for a specific package by specifying the <code>packageName</code> argument,
-     * for a specific UID if specifying the <code>uid</code> argument, for a
-     * specific package in a UID by specifying the <code>packageName</code>
-     * and the <code>uid</code> arguments, for all packages by passing
-     * {@link android.os.Process#INVALID_UID} and <code>null</code> for the
-     *  <code>uid</code> and <code>packageName</code> arguments, respectively.
-     *  Similarly, you can specify the <code>opNames</code> argument to get
-     *  data only for these ops or <code>null</code> for all ops.
-     *
-     * @param uid The UID to query for.
-     * @param packageName The package to query for.
-     * @param beginTimeMillis The beginning of the interval in milliseconds since
-     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
-     *     negative.
-     * @param endTimeMillis The end of the interval in milliseconds since
-     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
-     *     {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
-     *     history including ops that happen while this call is in flight.
-     * @param opNames The ops to query for. Pass {@code null} for all ops.
+     * @param request A request object describing the data being queried for.
      * @param executor Executor on which to run the callback. If <code>null</code>
      *     the callback is executed on the default executor running on the main thread.
      * @param callback Callback on which to deliver the result.
@@ -3701,13 +3794,13 @@
     @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    public void getHistoricalOps(int uid, @Nullable String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+    public void getHistoricalOps(@NonNull HistoricalOpsRequest request,
             @NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkNotNull(callback, "callback cannot be null");
         try {
-            mService.getHistoricalOps(uid, packageName, opNames, beginTimeMillis, endTimeMillis,
+            mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames,
+                    request.mBeginTimeMillis, request.mEndTimeMillis,
                     new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
@@ -3724,29 +3817,12 @@
 
     /**
      * Retrieve historical app op stats for a period.
-     *
-     * <p>Historical data can be obtained
-     * for a specific package by specifying the <code>packageName</code> argument,
-     * for a specific UID if specifying the <code>uid</code> argument, for a
-     * specific package in a UID by specifying the <code>packageName</code>
-     * and the <code>uid</code> arguments, for all packages by passing
-     * {@link android.os.Process#INVALID_UID} and <code>null</code> for the
-     *  <code>uid</code> and <code>packageName</code> arguments, respectively.
-     *  Similarly, you can specify the <code>opNames</code> argument to get
-     *  data only for these ops or <code>null</code> for all ops.
      *  <p>
      *  This method queries only the on disk state and the returned ops are raw,
      *  which is their times are relative to the history start as opposed to the
      *  epoch start.
      *
-     * @param uid The UID to query for.
-     * @param packageName The package to query for.
-     * @param beginTimeMillis The beginning of the interval in milliseconds since
-     *      history start. History time grows as one goes into the past.
-     * @param endTimeMillis The end of the interval in milliseconds since
-     *      history start. History time grows as one goes into the past. Must be after
-     *     {@code beginTimeMillis}.
-     * @param opNames The ops to query for. Pass {@code null} for all ops.
+     * @param request A request object describing the data being queried for.
      * @param executor Executor on which to run the callback. If <code>null</code>
      *     the callback is executed on the default executor running on the main thread.
      * @param callback Callback on which to deliver the result.
@@ -3757,15 +3833,15 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+    public void getHistoricalOpsFromDiskRaw(@NonNull HistoricalOpsRequest request,
             @Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkNotNull(callback, "callback cannot be null");
         try {
-            mService.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, beginTimeMillis,
-                    endTimeMillis, new RemoteCallback((result) -> {
-               final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
+            mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
+                    request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis,
+                    new RemoteCallback((result) -> {
+                final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() -> callback.accept(ops));
@@ -4290,12 +4366,35 @@
     /**
      * Like {@link #noteProxyOp(String, String)} but instead
      * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * <p>This API requires the package with the {@code proxiedPackageName} to belongs to
+     * {@link Binder#getCallingUid()}.
      */
     public int noteProxyOpNoThrow(String op, String proxiedPackageName) {
         return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName);
     }
 
     /**
+     * Like {@link #noteProxyOp(String, String)} but instead
+     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * <p>This API requires package with the {@code proxiedPackageName} to belong to
+     * {@code proxiedUid}.
+     *
+     * @param op The op to note
+     * @param proxiedPackageName The package to note the op for or {@code null} if the op should be
+     *                           noted for the "android" package
+     * @param proxiedUid The uid the package belongs to
+     *
+     * @hide
+     */
+    @SystemApi
+    public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
+            int proxiedUid) {
+        return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid);
+    }
+
+    /**
      * Report that an application has started executing a long-running operation.  Note that you
      * must pass in both the uid and name of the application to be checked; this function will
      * verify that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
@@ -4494,17 +4593,30 @@
      * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
      * @hide
      */
-    public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
+    public int noteProxyOpNoThrow(int op, String proxiedPackageName, int proxiedUid) {
         logOperationIfNeeded(op, mContext.getOpPackageName(), proxiedPackageName);
         try {
             return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
-                    Binder.getCallingUid(), proxiedPackageName);
+                    proxiedUid, proxiedPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Like {@link #noteProxyOp(int, String)} but instead
+     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * <p>This API requires the package with {@code proxiedPackageName} to belongs to
+     * {@link Binder#getCallingUid()}.
+     *
+     * @hide
+     */
+    public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
+        return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid());
+    }
+
+    /**
      * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
      * returns {@link #MODE_ERRORED}.
      * @hide
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 69c3632..062a462 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -33,6 +33,7 @@
     private int mMinManifestReceiverApiLevel = 0;
     private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
     private boolean mDontSendToRestrictedApps = false;
+    private boolean mAllowBackgroundActivityStarts;
 
     /**
      * How long to temporarily put an app on the power whitelist when executing this broadcast
@@ -54,11 +55,17 @@
             = "android:broadcast.maxManifestReceiverApiLevel";
 
     /**
-     * Corresponds to {@link #setMaxManifestReceiverApiLevel}.
+     * Corresponds to {@link #setDontSendToRestrictedApps}.
      */
     static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
             "android:broadcast.dontSendToRestrictedApps";
 
+    /**
+     * Corresponds to {@link #setAllowBackgroundActivityStarts}.
+     */
+    static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS =
+            "android:broadcast.allowBackgroundActivityStarts";
+
     public static BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
         return opts;
@@ -74,6 +81,8 @@
         mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
         mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false);
+        mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                false);
     }
 
     /**
@@ -148,6 +157,23 @@
     }
 
     /**
+     * Sets the process will be able to start activities from background for the duration of
+     * the broadcast dispatch. Default value is {@code false}
+     */
+    @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND)
+    public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
+        mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+    }
+
+    /**
+     * @hide
+     * @return #setAllowBackgroundActivityStarts
+     */
+    public boolean allowsBackgroundActivityStarts() {
+        return mAllowBackgroundActivityStarts;
+    }
+
+    /**
      * Returns the created options as a Bundle, which can be passed to
      * {@link android.content.Context#sendBroadcast(android.content.Intent)
      * Context.sendBroadcast(Intent)} and related methods.
@@ -169,6 +195,9 @@
         if (mDontSendToRestrictedApps) {
             b.putBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, true);
         }
+        if (mAllowBackgroundActivityStarts) {
+            b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true);
+        }
         return b.isEmpty() ? null : b;
     }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a728c1..6908ca2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2101,7 +2101,8 @@
     }
 
     private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
+            String[] overlayDirs) {
         final String[] splitResDirs;
         final ClassLoader classLoader;
         try {
@@ -2113,7 +2114,7 @@
         return ResourcesManager.getInstance().getResources(activityToken,
                 pi.getResDir(),
                 splitResDirs,
-                pi.getOverlayDirs(),
+                overlayDirs,
                 pi.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfig,
@@ -2131,9 +2132,11 @@
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
 
             final int displayId = getDisplayId();
-
+            // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have
+            // a LoadedApk containing Resources with stale overlays for a remote application.
+            final String[] overlayDirs = application.resourceDirs;
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs));
             if (c.mResources != null) {
                 return c;
             }
@@ -2168,7 +2171,7 @@
             final int displayId = getDisplayId();
 
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs()));
             if (c.mResources != null) {
                 return c;
             }
@@ -2218,7 +2221,8 @@
 
         final int displayId = getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+                mPackageInfo.getOverlayDirs()));
         return context;
     }
 
@@ -2233,7 +2237,8 @@
 
         final int displayId = display.getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+                mPackageInfo.getOverlayDirs()));
         context.mDisplay = display;
         return context;
     }
@@ -2416,7 +2421,7 @@
         ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null, null);
         context.setResources(createResources(null, packageInfo, null, displayId, null,
-                packageInfo.getCompatibilityInfo()));
+                packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs()));
         context.updateDisplay(displayId);
         return context;
     }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 2b765b2..497d5ba 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -215,7 +215,7 @@
     void registerTaskStackListener(in ITaskStackListener listener);
     void unregisterTaskStackListener(in ITaskStackListener listener);
     void setTaskResizeable(int taskId, int resizeableMode);
-    void exitFreeformMode(in IBinder token);
+    void toggleFreeformWindowingMode(in IBinder token);
     void resizeTask(int taskId, in Rect bounds, int resizeMode);
     void moveStackToDisplay(int stackId, int displayId);
     void removeStack(int stackId);
diff --git a/core/java/android/app/IInputForwarder.aidl b/core/java/android/app/IInputForwarder.aidl
deleted file mode 100644
index d6be63e..0000000
--- a/core/java/android/app/IInputForwarder.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.view.InputEvent;
-
-/**
- * Forwards input events into owned activity container, used in {@link android.app.ActivityView}.
- * To forward input to other apps {@link android.Manifest.permission.INJECT_EVENTS} permission is
- * required.
- * @hide
- */
-interface IInputForwarder {
-    boolean forwardEvent(in InputEvent event);
-}
\ No newline at end of file
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d46dbed..5c4c005 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -870,8 +870,9 @@
             }
         }
 
-        // /vendor/lib, /odm/lib and /product/lib are added to the native lib search
-        // paths of the classloader. Note that this is done AFTER the classloader is
+        // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+        // are added to the native lib search paths of the classloader.
+        // Note that this is done AFTER the classloader is
         // created by ApplicationLoaders.getDefault().getClassLoader(...). The
         // reason is because if we have added the paths when creating the classloader
         // above, the paths are also added to the search path of the linker namespace
@@ -888,8 +889,11 @@
         // System.loadLibrary(). In order to prevent the problem, we explicitly
         // add the paths only to the classloader, and not to the native loader
         // (linker namespace).
-        List<String> extraLibPaths = new ArrayList<>(3);
+        List<String> extraLibPaths = new ArrayList<>(4);
         String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
+        if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
+            extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+        }
         if (!defaultSearchPaths.contains("/vendor/lib")) {
             extraLibPaths.add("/vendor/lib" + abiSuffix);
         }
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 87bf5ed..f116e13 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.pm.ServiceInfo.ForegroundServiceType;
 import android.content.res.Configuration;
 import android.os.Build;
 import android.os.IBinder;
@@ -735,7 +736,7 @@
    * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags.
    */
     public final void startForeground(int id, @NonNull Notification notification,
-            int foregroundServiceType) {
+            @ForegroundServiceType int foregroundServiceType) {
         try {
             mActivityManager.setServiceForeground(
                     new ComponentName(this, mClassName), mToken, id,
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1878d84..077652c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -42,12 +42,10 @@
 public class StatusBarManager {
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
     /** @hide */
     public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_NOTIFICATION_ALERTS
             = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
 
@@ -59,17 +57,14 @@
     /** @hide */
     public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
     /** @hide */
     public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
     /** @hide */
     public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
 
     /** @hide */
@@ -78,7 +73,6 @@
             View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE_NONE = 0x00000000;
 
     /** @hide */
@@ -122,7 +116,6 @@
     public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE2_NONE = 0x00000000;
 
     /** @hide */
@@ -387,14 +380,14 @@
     }
 
     /**
-     * Get the currently applied StatusBar disable flags
+     * Get this app's currently requested disabled components
      *
-     * @return a pair of Integers in the form of (disable, disable2)
+     * @return a new DisableInfo
      *
      * @hide
      */
     @SystemApi
-    public Pair<Integer, Integer> getDisableFlags() {
+    public DisableInfo getDisableInfo() {
         try {
             final int userId = Binder.getCallingUserHandle().getIdentifier();
             final IStatusBarService svc = getService();
@@ -403,7 +396,7 @@
                 flags = svc.getDisableFlags(mToken, userId);
             }
 
-            return new Pair<Integer, Integer>(flags[0], flags[1]);
+            return new DisableInfo(flags[0], flags[1]);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -416,4 +409,180 @@
         if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
         return "WINDOW_STATE_UNKNOWN";
     }
+
+    /**
+     * DisableInfo describes this app's requested state of the StatusBar with regards to which
+     * components are enabled/disabled
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class DisableInfo {
+
+        private boolean mStatusBarExpansion;
+        private boolean mNavigateHome;
+        private boolean mNotificationPeeking;
+        private boolean mRecents;
+        private boolean mSearch;
+
+        /** @hide */
+        public DisableInfo(int flags1, int flags2) {
+            mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
+            mNavigateHome = (flags1 & DISABLE_HOME) != 0;
+            mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
+            mRecents = (flags1 & DISABLE_RECENT) != 0;
+            mSearch = (flags1 & DISABLE_SEARCH) != 0;
+        }
+
+        /** @hide */
+        public DisableInfo() {}
+
+        /**
+         * @return {@code true} if expanding the notification shade is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isStatusBarExpansionDisabled() {
+            return mStatusBarExpansion;
+        }
+
+        /** * @hide */
+        public void setStatusBarExpansionDisabled(boolean disabled) {
+            mStatusBarExpansion = disabled;
+        }
+
+        /**
+         * @return {@code true} if navigation home is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isNavigateToHomeDisabled() {
+            return mNavigateHome;
+        }
+
+        /** * @hide */
+        public void setNagivationHomeDisabled(boolean disabled) {
+            mNavigateHome = disabled;
+        }
+
+        /**
+         * @return {@code true} if notification peeking (heads-up notification) is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isNotificationPeekingDisabled() {
+            return mNotificationPeeking;
+        }
+
+        /** @hide */
+        public void setNotificationPeekingDisabled(boolean disabled) {
+            mNotificationPeeking = disabled;
+        }
+
+        /**
+         * @return {@code true} if mRecents/overview is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isRecentsDisabled() {
+            return mRecents;
+        }
+
+        /**  @hide */
+        public void setRecentsDisabled(boolean disabled) {
+            mRecents = disabled;
+        }
+
+        /**
+         * @return {@code true} if mSearch is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isSearchDisabled() {
+            return mSearch;
+        }
+
+        /** @hide */
+        public void setSearchDisabled(boolean disabled) {
+            mSearch = disabled;
+        }
+
+        /**
+         * @return {@code true} if no components are disabled (default state)
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean areNoComponentsDisabled() {
+            return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
+                    && !mSearch;
+        }
+
+        /** @hide */
+        public void setEnableAll() {
+            mStatusBarExpansion = false;
+            mNavigateHome = false;
+            mNotificationPeeking = false;
+            mRecents = false;
+            mSearch = false;
+        }
+
+        /**
+         * @return {@code true} if all status bar components are disabled
+         *
+         * @hide
+         */
+        public boolean areAllComponentsDisabled() {
+            return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
+                    && mRecents && mSearch;
+        }
+
+        /** @hide */
+        public void setDisableAll() {
+            mStatusBarExpansion = true;
+            mNavigateHome = true;
+            mNotificationPeeking = true;
+            mRecents = true;
+            mSearch = true;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("DisableInfo: ");
+            sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
+            sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
+            sb.append(" mNotificationPeeking=")
+                    .append(mNotificationPeeking ? "disabled" : "enabled");
+            sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
+            sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
+
+            return sb.toString();
+
+        }
+
+        /**
+         * Convert a DisableInfo to equivalent flags
+         * @return a pair of equivalent disable flags
+         *
+         * @hide
+         */
+        public Pair<Integer, Integer> toFlags() {
+            int disable1 = DISABLE_NONE;
+            int disable2 = DISABLE2_NONE;
+
+            if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND;
+            if (mNavigateHome) disable1 |= DISABLE_HOME;
+            if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
+            if (mRecents) disable1 |= DISABLE_RECENT;
+            if (mSearch) disable1 |= DISABLE_SEARCH;
+
+            return new Pair<Integer, Integer>(disable1, disable2);
+        }
+    }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 807b7f2..c12a92f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,13 +101,11 @@
 import android.net.IEthernetManager;
 import android.net.IIpMemoryStore;
 import android.net.IIpSecService;
-import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.IpMemoryStore;
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
-import android.net.NetworkStack;
 import android.net.NetworkWatchlistManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
@@ -330,21 +328,13 @@
                 return new ConnectivityManager(context, service);
             }});
 
-        registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() {
+        registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
             @Override
-            public INetd createService() throws ServiceNotFoundException {
-                return INetd.Stub.asInterface(
-                        ServiceManager.getServiceOrThrow(Context.NETD_SERVICE));
+            public IBinder createService() throws ServiceNotFoundException {
+                return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE);
             }
         });
 
-        registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
-                new StaticServiceFetcher<NetworkStack>() {
-                    @Override
-                    public NetworkStack createService() {
-                        return new NetworkStack();
-                    }});
-
         registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
                 new CachedServiceFetcher<IpMemoryStore>() {
                     @Override
@@ -1139,7 +1129,11 @@
                     IBinder b = ServiceManager
                             .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
                     IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
-                    return new ContentCaptureManager(outerContext, service);
+                    if (service != null) {
+                        // When feature is disabled, we return a null manager to apps so the
+                        // performance impact is practically zero
+                        return new ContentCaptureManager(outerContext, service);
+                    }
                 }
                 return null;
             }});
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a554882..46316e1 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -229,7 +229,11 @@
      * <strong>Note:</strong> On API 22 and below, changes to the night mode
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
-     * device. Starting in API 23, changes to night mode are always effective.
+     * device. On API 23 through API 28, changes to night mode are always effective.
+     * <p>
+     * Starting in API 29, when the device is in car mode and this method is called, night mode
+     * will change, but the new setting is not persisted and the previously persisted setting
+     * will be restored when the device exits car mode.
      * <p>
      * Changes to night mode take effect globally and will result in a configuration change
      * (and potentially an Activity lifecycle event) being applied to all running apps.
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index f0f7d89..28c79aa 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -371,10 +371,17 @@
      * Returns whether this wallpaper service can support multiple engines to render on each surface
      * independently. An example use case is a multi-display set-up where the wallpaper service can
      * render surfaces to each of the connected displays.
+     * <p>
+     * This corresponds to the value {@link android.R.styleable#Wallpaper_supportsMultipleDisplays}
+     * in the XML description of the wallpaper.
+     * <p>
+     * The default value is {@code false}.
      *
      * @see WallpaperService#onCreateEngine()
      * @see WallpaperService.Engine#onCreate(SurfaceHolder)
      * @return {@code true} if multiple engines can render independently on each surface.
+     *
+     * @attr ref android.R.styleable#Wallpaper_supportsMultipleDisplays
      */
     public boolean supportsMultipleDisplays() {
         return mSupportMultipleDisplays;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 478ac9c..3587c68 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2125,7 +2125,7 @@
      * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
      * trying to install an update.
      */
-    public abstract static class InstallUpdateCallback {
+    public abstract static class InstallSystemUpdateCallback {
         /** Represents an unknown error while trying to install an update. */
         public static final int UPDATE_ERROR_UNKNOWN = 1;
 
@@ -2144,7 +2144,12 @@
         /** Represents the battery being too low to apply an update. */
         public static final int UPDATE_ERROR_BATTERY_LOW = 5;
 
-        /** Method invoked when there was an error while installing an update. */
+        /**
+         * Method invoked when there was an error while installing an update.
+         *
+         * <p>The given error message is not intended to be user-facing. It is intended to be
+         * reported back to the IT admin to be read.
+         */
         public void onInstallUpdateError(
                 @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
         }
@@ -2154,11 +2159,11 @@
      * @hide
      */
     @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
-            InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
-            InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
-            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
-            InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
-            InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+            InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
+            InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+            InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+            InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+            InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallUpdateCallbackErrorConstants {}
@@ -7031,9 +7036,9 @@
      }
 
     /**
-     * Called by a profile or device owner to set the permitted input methods services. When set by
-     * a device owner or profile owner the restriction applies to all profiles of the user the
-     * device owner or profile owner is an admin for. By default, the user can use any input method.
+     * Called by a profile or device owner to set the permitted input methods services for this
+     * user. By default, the user can use any input method.
+     * <p>
      * When zero or more packages have been added, input method that are not in the list and not
      * part of the system can not be enabled by the user. This method will fail if it is called for
      * a admin that is not for the foreground user or a profile of the foreground user. Any
@@ -7042,7 +7047,7 @@
      * Calling with a null value for the list disables the restriction so that all input methods can
      * be used, calling with an empty list disables all but the system's own input methods.
      * <p>
-     * System input methods are always available to the user this method can't modify this.
+     * System input methods are always available to the user - this method can't modify this.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames List of input method package names.
@@ -10431,9 +10436,9 @@
      * doesn't necessarily mean that the update has been applied successfully. The caller should
      * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
      * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
-     * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+     * the caller will be notified by {@link InstallSystemUpdateCallback}. If device does not have
      * sufficient battery level, the installation will fail with error {@link
-     * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+     * InstallSystemUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
      *
      * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
      * @param updateFilePath An Uri of the file that contains the update. The file should be
@@ -10445,7 +10450,7 @@
     public void installSystemUpdate(
             @NonNull ComponentName admin, @NonNull Uri updateFilePath,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull InstallUpdateCallback callback) {
+            @NonNull InstallSystemUpdateCallback callback) {
         throwIfParentInstance("installUpdate");
         if (mService == null) {
             return;
@@ -10465,19 +10470,20 @@
         } catch (FileNotFoundException e) {
             Log.w(TAG, e);
             executeCallback(
-                    InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+                    InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+                    Log.getStackTraceString(e),
                     executor, callback);
         } catch (IOException e) {
             Log.w(TAG, e);
             executeCallback(
-                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
                     executor, callback);
         }
     }
 
     private void executeCallback(int errorCode, String errorMessage,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull InstallUpdateCallback callback) {
+            @NonNull InstallSystemUpdateCallback callback) {
         executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
     }
 
@@ -10537,6 +10543,8 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS,
+            conditional = true)
     public void setProfileOwnerCanAccessDeviceIdsForUser(
             @NonNull ComponentName who, @NonNull UserHandle userHandle) {
         if (mService == null) {
diff --git a/core/java/android/app/admin/TEST_MAPPING b/core/java/android/app/admin/TEST_MAPPING
new file mode 100644
index 0000000..8f88c22
--- /dev/null
+++ b/core/java/android/app/admin/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/services/devicepolicy"
+    }
+  ]
+}
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 94a2a3e..97efa01 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -465,8 +465,6 @@
                 mActivities.put(instanceId, eventType);
                 break;
             case ACTIVITY_STOPPED:
-                mActivities.put(instanceId, eventType);
-                break;
             case ACTIVITY_DESTROYED:
                 // remove activity from the map.
                 mActivities.delete(instanceId);
diff --git a/core/java/android/content/ContentInterface.java b/core/java/android/content/ContentInterface.java
index 3d732eb..d41d8d9 100644
--- a/core/java/android/content/ContentInterface.java
+++ b/core/java/android/content/ContentInterface.java
@@ -36,6 +36,8 @@
  * These methods have been extracted into a general interface so that APIs can
  * be flexible in accepting either a {@link ContentProvider}, a
  * {@link ContentResolver}, or a {@link ContentProviderClient}.
+ *
+ * @hide
  */
 public interface ContentInterface {
     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index f138d39..e06322df 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -134,7 +134,7 @@
     private boolean mNoPerms;
     private boolean mSingleUser;
 
-    private final ThreadLocal<String> mCallingPackage = new ThreadLocal<>();
+    private ThreadLocal<String> mCallingPackage;
 
     private Transport mTransport = new Transport();
 
@@ -2034,6 +2034,7 @@
 
     private void attachInfo(Context context, ProviderInfo info, boolean testing) {
         mNoPerms = testing;
+        mCallingPackage = new ThreadLocal<>();
 
         /*
          * Only allow it to be set once, so after the content service gives
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 0b5bdb5..93bf518 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -626,15 +626,14 @@
         return ContentProvider.coerceToLocalContentProvider(mContentProvider);
     }
 
-    /**
-     * Closes the given object quietly, ignoring any checked exceptions. Does
-     * nothing if the given object is {@code null}.
-     */
+    /** {@hide} */
+    @Deprecated
     public static void closeQuietly(ContentProviderClient client) {
         IoUtils.closeQuietly(client);
     }
 
     /** {@hide} */
+    @Deprecated
     public static void releaseQuietly(ContentProviderClient client) {
         IoUtils.closeQuietly(client);
     }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f1bfe86..0e11d4e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -609,10 +609,60 @@
     private static final int SLOW_THRESHOLD_MILLIS = 500;
     private final Random mRandom = new Random();  // guarded by itself
 
-    public ContentResolver(Context context) {
+    public ContentResolver(@Nullable Context context) {
+        this(context, null);
+    }
+
+    /** {@hide} */
+    public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
         mContext = context != null ? context : ActivityThread.currentApplication();
         mPackageName = mContext.getOpPackageName();
         mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        mWrapped = wrapped;
+    }
+
+    /** {@hide} */
+    public static ContentResolver wrap(@NonNull ContentInterface wrapped) {
+        Preconditions.checkNotNull(wrapped);
+
+        return new ContentResolver(null, wrapped) {
+            @Override
+            public void unstableProviderDied(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public boolean releaseUnstableProvider(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public boolean releaseProvider(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            protected IContentProvider acquireUnstableProvider(Context c, String name) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            protected IContentProvider acquireProvider(Context c, String name) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Create a {@link ContentResolver} instance that redirects all its methods
+     * to the given {@link ContentProvider}.
+     */
+    public static ContentResolver wrap(@NonNull ContentProvider wrapped) {
+        return wrap((ContentInterface) wrapped);
+    }
+
+    /**
+     * Create a {@link ContentResolver} instance that redirects all its methods
+     * to the given {@link ContentProviderClient}.
+     */
+    public static ContentResolver wrap(@NonNull ContentProviderClient wrapped) {
+        return wrap((ContentInterface) wrapped);
     }
 
     /** @hide */
@@ -660,6 +710,12 @@
     public final @Nullable String getType(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
 
+        try {
+            if (mWrapped != null) return mWrapped.getType(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         // XXX would like to have an acquireExistingUnstableProvider for this.
         IContentProvider provider = acquireExistingProvider(url);
         if (provider != null) {
@@ -715,6 +771,12 @@
         Preconditions.checkNotNull(url, "url");
         Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
 
+        try {
+            if (mWrapped != null) return mWrapped.getStreamTypes(url, mimeTypeFilter);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             return null;
@@ -843,6 +905,15 @@
             @Nullable String[] projection, @Nullable Bundle queryArgs,
             @Nullable CancellationSignal cancellationSignal) {
         Preconditions.checkNotNull(uri, "uri");
+
+        try {
+            if (mWrapped != null) {
+                return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
+            }
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider unstableProvider = acquireUnstableProvider(uri);
         if (unstableProvider == null) {
             return null;
@@ -942,6 +1013,13 @@
     @Override
     public final @Nullable Uri canonicalize(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.canonicalize(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             return null;
@@ -979,6 +1057,13 @@
     @Override
     public final @Nullable Uri uncanonicalize(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.uncanonicalize(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             return null;
@@ -1015,6 +1100,13 @@
     public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
             @Nullable CancellationSignal cancellationSignal) {
         Preconditions.checkNotNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.refresh(url, args, cancellationSignal);
+        } catch (RemoteException e) {
+            return false;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             return false;
@@ -1126,6 +1218,12 @@
     @Override
     public final @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) return mWrapped.openFile(uri, mode, signal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         return openFileDescriptor(uri, mode, signal);
     }
 
@@ -1237,6 +1335,12 @@
     @Override
     public final @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, signal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         return openAssetFileDescriptor(uri, mode, signal);
     }
 
@@ -1448,6 +1552,14 @@
     public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
             @NonNull String mimeTypeFilter, @Nullable Bundle opts,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) {
+                return mWrapped.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
+            }
+        } catch (RemoteException e) {
+            return null;
+        }
+
         return openTypedAssetFileDescriptor(uri, mimeTypeFilter, opts, signal);
     }
 
@@ -1664,6 +1776,13 @@
     public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
                 @Nullable ContentValues values) {
         Preconditions.checkNotNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.insert(url, values);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown URL " + url);
@@ -1705,6 +1824,13 @@
                     throws RemoteException, OperationApplicationException {
         Preconditions.checkNotNull(authority, "authority");
         Preconditions.checkNotNull(operations, "operations");
+
+        try {
+            if (mWrapped != null) return mWrapped.applyBatch(authority, operations);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         ContentProviderClient provider = acquireContentProviderClient(authority);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown authority " + authority);
@@ -1731,6 +1857,13 @@
                 @NonNull ContentValues[] values) {
         Preconditions.checkNotNull(url, "url");
         Preconditions.checkNotNull(values, "values");
+
+        try {
+            if (mWrapped != null) return mWrapped.bulkInsert(url, values);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown URL " + url);
@@ -1764,6 +1897,13 @@
     public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
             @Nullable String[] selectionArgs) {
         Preconditions.checkNotNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.delete(url, where, selectionArgs);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
         IContentProvider provider = acquireProvider(url);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown URL " + url);
@@ -1801,6 +1941,13 @@
             @Nullable ContentValues values, @Nullable String where,
             @Nullable String[] selectionArgs) {
         Preconditions.checkNotNull(uri, "uri");
+
+        try {
+            if (mWrapped != null) return mWrapped.update(uri, values, where, selectionArgs);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
         IContentProvider provider = acquireProvider(uri);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown URI " + uri);
@@ -1844,6 +1991,13 @@
             @Nullable String arg, @Nullable Bundle extras) {
         Preconditions.checkNotNull(authority, "authority");
         Preconditions.checkNotNull(method, "method");
+
+        try {
+            if (mWrapped != null) return mWrapped.call(authority, method, arg, extras);
+        } catch (RemoteException e) {
+            return null;
+        }
+
         IContentProvider provider = acquireProvider(authority);
         if (provider == null) {
             throw new IllegalArgumentException("Unknown authority " + authority);
@@ -2994,12 +3148,17 @@
     }
 
     /**
-     * Put the cache with the key.
+     * Store the given {@link Bundle} as a long-lived cached object within the
+     * system. This can be useful to avoid expensive re-parsing when apps are
+     * restarted multiple times on low-RAM devices.
+     * <p>
+     * The {@link Bundle} is automatically invalidated when a
+     * {@link #notifyChange(Uri, ContentObserver)} event applies to the key.
      *
-     * @param key the key to add
-     * @param value the value to add
+     * @hide
      */
-    public void putCache(Uri key, Bundle value) {
+    @SystemApi
+    public void putCache(@NonNull Uri key, @Nullable Bundle value) {
         try {
             getContentService().putCache(mContext.getPackageName(), key, value,
                     mContext.getUserId());
@@ -3009,13 +3168,16 @@
     }
 
     /**
-     * Get the cache with the key.
+     * Retrieve the last {@link Bundle} stored as a long-lived cached object
+     * within the system.
      *
-     * @param key the key to get the value
-     * @return the matched value. If the key doesn't exist, will return null.
-     * @see #putCache(Uri, Bundle)
+     * @return {@code null} if no cached object has been stored, or if the
+     *         stored object has been invalidated due to a
+     *         {@link #notifyChange(Uri, ContentObserver)} event.
+     * @hide
      */
-    public Bundle getCache(Uri key) {
+    @SystemApi
+    public @Nullable Bundle getCache(@NonNull Uri key) {
         try {
             final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
                     mContext.getUserId());
@@ -3189,6 +3351,7 @@
     @UnsupportedAppUsage
     final String mPackageName;
     final int mTargetSdkVersion;
+    final ContentInterface mWrapped;
 
     private static final String TAG = "ContentResolver";
 
diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java
index fd7b372..767d3f6 100644
--- a/core/java/android/content/ContentUris.java
+++ b/core/java/android/content/ContentUris.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.NonNull;
 import android.net.Uri;
 
 import java.util.List;
@@ -83,7 +84,7 @@
      * @return the long conversion of the last segment or -1 if the path is
      *  empty
      */
-    public static long parseId(Uri contentUri) {
+    public static long parseId(@NonNull Uri contentUri) {
         String last = contentUri.getLastPathSegment();
         return last == null ? -1 : Long.parseLong(last);
     }
@@ -96,7 +97,7 @@
      *
      * @return the given builder
      */
-    public static Uri.Builder appendId(Uri.Builder builder, long id) {
+    public static @NonNull Uri.Builder appendId(@NonNull Uri.Builder builder, long id) {
         return builder.appendEncodedPath(String.valueOf(id));
     }
 
@@ -108,7 +109,7 @@
      *
      * @return a new URI with the given ID appended to the end of the path
      */
-    public static Uri withAppendedId(Uri contentUri, long id) {
+    public static @NonNull Uri withAppendedId(@NonNull Uri contentUri, long id) {
         return appendId(contentUri.buildUpon(), id).build();
     }
 
@@ -120,7 +121,7 @@
      * @throws IllegalArgumentException when the given URI has no ID to remove
      *             from the end of the path
      */
-    public static Uri removeId(Uri contentUri) {
+    public static @NonNull Uri removeId(@NonNull Uri contentUri) {
         // Verify that we have a valid ID to actually remove
         final String last = contentUri.getLastPathSegment();
         if (last == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957a484..25bfba2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,7 +49,6 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.net.NetworkStack;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -3647,11 +3646,10 @@
     public static final String NETD_SERVICE = "netd";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link NetworkStack} for communicating with the network stack
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link NetworkStackClient} IBinder for communicating with the network stack
      * @hide
-     * @see #getSystemService(String)
-     * @see NetworkStack
+     * @see NetworkStackClient
      */
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d781a96..b67349f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -37,6 +37,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.media.MediaScannerConnection;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -1790,6 +1791,35 @@
             "android.intent.action.MANAGE_APP_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage a specific permissions of an app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
+     * will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
+     * that should be managed by the launched UI.
+     * </p>
+     * <p>
+     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_USER
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_PERMISSION =
+            "android.intent.action.MANAGE_APP_PERMISSION";
+
+    /**
      * Activity action: Launch UI to manage permissions.
      * <p>
      * Input: Nothing.
@@ -2080,6 +2110,22 @@
     public static final String ACTION_REVIEW_APP_PERMISSION_USAGE =
             "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
 
+    /**
+     * Activity action: Launch UI to review running accessibility services.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES =
+            "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -3049,18 +3095,25 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED";
 
-   /**
-     * Broadcast Action:  Request the media scanner to scan a file and add it to the media database.
-     * The path to the file is contained in the Intent.mData field.
+    /**
+     * Broadcast Action: Request the media scanner to scan a file and add it to
+     * the media database.
+     * <p>
+     * The path to the file is contained in {@link Intent#getData()}.
+     *
+     * @deprecated Starting in the {@link android.os.Build.VERSION_CODES#Q}
+     *             release, shared storage paths are sandboxed per application,
+     *             and this broadcast cannot correctly translate those sandboxed
+     *             paths. Callers will need to instead migrate to using
+     *             {@link MediaScannerConnection}.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
 
-    /**
-     * Broadcast Action:  Request the media scanner to scan a storage volume and add it to the media database.
-     * The path to the storage volume is contained in the Intent.mData field.
-     */
+    /** @hide */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
 
    /**
@@ -4291,6 +4344,18 @@
             "android.intent.action.DEVICE_CUSTOMIZATION_READY";
 
 
+    /**
+     * Activity Action: Display an activity state associated with an unique {@link LocusId}.
+     *
+     * <p>For example, a chat app could use the context to resume a conversation between 2 users.
+     *
+     * <p>Input: {@link #EXTRA_LOCUS_ID} specifies the unique identifier of the locus in the
+     * app domain. Should be stable across reboots and backup / restore.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
@@ -5489,6 +5554,15 @@
      */
     public static final int EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC = 1;
 
+    /**
+     * Intent extra: ID of the context used on {@link #ACTION_VIEW_LOCUS}.
+     *
+     * <p>
+     * Type: {@link LocusId}
+     * </p>
+     */
+    public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java
new file mode 100644
index 0000000..9548f9c
--- /dev/null
+++ b/core/java/android/content/LocusId.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+
+/**
+ * Identifier for an unique state in the application.
+ *
+ * <p>Should be stable across reboots and backup / restore.
+ *
+ * <p>For example, a chat app could use the context to resume a conversation between 2 users.
+ */
+// TODO(b/123577059): make sure this is well documented and understandable
+public final class LocusId implements Parcelable {
+
+    private final Uri mUri;
+
+    /**
+     * Default constructor.
+     */
+    public LocusId(@NonNull Uri uri) {
+        mUri = Preconditions.checkNotNull(uri);
+    }
+
+    /**
+     * Gets the {@code uri} associated with the locus.
+     */
+    @NonNull
+    public Uri getUri() {
+        return mUri;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mUri == null) ? 0 : mUri.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;
+        final LocusId other = (LocusId) obj;
+        if (mUri == null) {
+            if (other.mUri != null) return false;
+        } else {
+            if (!mUri.equals(other.mUri)) return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "LocusId[uri=" + getSanitizedUri() + "]";
+    }
+
+    /** @hide */
+    public void dump(@NonNull PrintWriter pw) {
+        pw.print("uri:"); pw.println(getSanitizedUri());
+    }
+
+    private String getSanitizedUri() {
+        final int size = mUri.toString().length();
+        return size + "_chars";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mUri, flags);
+    }
+
+    public static final Parcelable.Creator<LocusId> CREATOR =
+            new Parcelable.Creator<LocusId>() {
+
+        @Override
+        public LocusId createFromParcel(Parcel source) {
+            final Uri uri = source.readParcelable(null);
+            return new LocusId(uri);
+        }
+
+        @Override
+        public LocusId[] newArray(int size) {
+            return new LocusId[size];
+        }
+    };
+}
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 9f5c877..6fe6e99 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -108,8 +108,7 @@
             packageName = packageNames[0];
         }
 
-        if (appOpsManager.noteProxyOpNoThrow(op, packageName)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid) != AppOpsManager.MODE_ALLOWED) {
             return PERMISSION_DENIED_APP_OP;
         }
 
@@ -120,6 +119,9 @@
      * Checks whether your app has a given permission and whether the app op
      * that corresponds to this permission is allowed.
      *
+     * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+     * {@link Process#myUid()}.
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 36ffb0e..14e7725 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -295,8 +295,6 @@
     void restoreDefaultApps(in byte[] backup, int userId);
     byte[] getIntentFilterVerificationBackup(int userId);
     void restoreIntentFilterVerification(in byte[] backup, int userId);
-    byte[] getPermissionGrantBackup(int userId);
-    void restorePermissionGrants(in byte[] backup, int userId);
 
     /**
      * Report the set of 'Home' activity candidates, plus (if any) which of them
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index eaf6c5a..0041921 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2943,6 +2943,7 @@
     * @hide
     */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
 
     /**
@@ -2953,6 +2954,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
 
     /**
@@ -2976,6 +2978,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
 
     /**
@@ -3005,6 +3008,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
 
     /**
@@ -3014,6 +3018,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED =  1 << 7;
 
     /**
@@ -3795,6 +3800,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -3815,6 +3821,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -5910,24 +5917,24 @@
     /**
      * Flag to denote no restrictions. This should be used to clear any restrictions that may have
      * been previously set for the package.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_NONE = 0x0;
 
     /**
      * Flag to denote that a package should be hidden from any suggestions to the user.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
 
     /**
      * Flag to denote that a package's notifications should be hidden.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
@@ -5939,7 +5946,6 @@
      * @see #setDistractingPackageRestrictions(String[], int)
      * @hide
      */
-    @SystemApi
     @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
             RESTRICTION_NONE,
             RESTRICTION_HIDE_FROM_SUGGESTIONS,
@@ -5957,14 +5963,16 @@
      * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
      *
      * @param packages Packages to mark as distracting.
-     * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
-     *                         impose on the given packages. {@link #RESTRICTION_NONE} can be used
-     *                         to clear any existing restrictions.
+     * @param restrictionFlags Any combination of restrictions to impose on the given packages.
+     *                         {@link #RESTRICTION_NONE} can be used to clear any existing
+     *                         restrictions.
      * @return A list of packages that could not have the {@code restrictionFlags} set. The system
      * may prevent restricting critical packages to preserve normal device function.
      *
-     * @see DistractionRestriction
      * @hide
+     * @see #RESTRICTION_NONE
+     * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS
+     * @see #RESTRICTION_HIDE_NOTIFICATIONS
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index cfe35b0..270e387 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -163,6 +163,30 @@
     }
 
     /**
+     * Provider for default home
+     */
+    public interface DefaultHomeProvider {
+
+        /**
+         * Get the package name of the default home.
+         *
+         * @param userId the user id
+         *
+         * @return the package name of the default home, or {@code null} if none
+         */
+        @Nullable
+        String getDefaultHome(@UserIdInt int userId);
+
+        /**
+         * Set the package name of the default home.
+         *
+         * @param packageName package name of the default home, or {@code null} to remove
+         * @param userId the user id
+         */
+        void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId);
+    }
+
+    /**
      * Sets the location provider packages provider.
      * @param provider The packages provider.
      */
@@ -886,4 +910,11 @@
      * @param provider the provider
      */
     public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider);
+
+    /**
+     * Sets the default home provider.
+     *
+     * @param provider the provider
+     */
+    public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0abd5ea..0f67262 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4848,7 +4848,7 @@
     }
 
     /**
-     * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+     * Sets every the min aspect ratio of every child activity that doesn't already have an aspect
      * ratio set.
      */
     private void setMinAspectRatio(Package owner) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 60475de..4a2f800 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -151,6 +151,7 @@
      * @hide
      */
     @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+            FOREGROUND_SERVICE_TYPE_MANIFEST,
             FOREGROUND_SERVICE_TYPE_NONE,
             FOREGROUND_SERVICE_TYPE_DATA_SYNC,
             FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
@@ -180,10 +181,10 @@
     }
 
     /**
-     * Return the current foreground service type.
-     * @return the current foreground service type.
+     * Return foreground service type specified in the manifest..
+     * @return foreground service type specified in the manifest.
      */
-    public int getForegroundServiceType() {
+    public @ForegroundServiceType int getForegroundServiceType() {
         return mForegroundServiceType;
     }
 
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index fe68b8a..b155325 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2273,7 +2273,8 @@
             CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
             Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
             long lastChangedTimestamp,
-            int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) {
+            int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason,
+            Person[] persons) {
         mUserId = userId;
         mId = id;
         mPackageName = packageName;
@@ -2299,5 +2300,6 @@
         mIconResName = iconResName;
         mBitmapPath = bitmapPath;
         mDisabledReason = disabledReason;
+        mPersons = persons;
     }
 }
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index ceeecbc..0e869c8 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -48,6 +48,9 @@
  *
  * <p class="note"><strong>Note:</strong> this class assumes
  * monotonically increasing version numbers for upgrades.</p>
+ *
+ * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was
+ * first added in the {@link android.os.Build.VERSION_CODES#Q} release.</p>
  */
 public abstract class SQLiteOpenHelper implements AutoCloseable {
     private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 5722e7b..03e8507 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -68,15 +68,23 @@
     }
 
     /**
-     * Mark the query as DISTINCT.
+     * Mark the query as {@code DISTINCT}.
      *
-     * @param distinct if true the query is DISTINCT, otherwise it isn't
+     * @param distinct if true the query is {@code DISTINCT}, otherwise it isn't
      */
     public void setDistinct(boolean distinct) {
         mDistinct = distinct;
     }
 
     /**
+     * Get if the query is marked as {@code DISTINCT}, as last configured by
+     * {@link #setDistinct(boolean)}.
+     */
+    public boolean getDistinct() {
+        return mDistinct;
+    }
+
+    /**
      * Returns the list of tables being queried
      *
      * @return the list of tables being queried
@@ -98,13 +106,13 @@
     }
 
     /**
-     * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
-     * by parenthesis and ANDed with the selection passed to {@link #query}. The final
-     * WHERE clause looks like:
-     *
+     * Append a chunk to the {@code WHERE} clause of the query. All chunks appended are surrounded
+     * by parenthesis and {@code AND}ed with the selection passed to {@link #query}. The final
+     * {@code WHERE} clause looks like:
+     * <p>
      * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
      *
-     * @param inWhere the chunk of text to append to the WHERE clause.
+     * @param inWhere the chunk of text to append to the {@code WHERE} clause.
      */
     public void appendWhere(@NonNull CharSequence inWhere) {
         if (mWhereClause == null) {
@@ -114,13 +122,13 @@
     }
 
     /**
-     * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
+     * Append a chunk to the {@code WHERE} clause of the query. All chunks appended are surrounded
      * by parenthesis and ANDed with the selection passed to {@link #query}. The final
-     * WHERE clause looks like:
-     *
+     * {@code WHERE} clause looks like:
+     * <p>
      * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
      *
-     * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
+     * @param inWhere the chunk of text to append to the {@code WHERE} clause. it will be escaped
      * to avoid SQL injection attacks
      */
     public void appendWhereEscapeString(@NonNull String inWhere) {
@@ -167,6 +175,14 @@
     }
 
     /**
+     * Gets the projection map for the query, as last configured by
+     * {@link #setProjectionMap(Map)}.
+     */
+    public Map<String, String> getProjectionMap() {
+        return mProjectionMap;
+    }
+
+    /**
      * Sets a projection greylist of columns that will be allowed through, even
      * when {@link #setStrict(boolean)} is enabled. This provides a way for
      * abusive custom columns like {@code COUNT(*)} to continue working.
@@ -178,6 +194,16 @@
     }
 
     /**
+     * Gets the projection greylist for the query, as last configured by
+     * {@link #setProjectionGreylist(List)}.
+     *
+     * @hide
+     */
+    public List<Pattern> getProjectionGreylist() {
+        return mProjectionGreylist;
+    }
+
+    /**
      * Sets the cursor factory to be used for the query.  You can use
      * one factory for all queries on a database but it is normally
      * easier to specify the factory when doing this query.
@@ -189,6 +215,14 @@
     }
 
     /**
+     * Sets the cursor factory to be used for the query, as last configured by
+     * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
+     */
+    public SQLiteDatabase.CursorFactory getCursorFactory() {
+        return mFactory;
+    }
+
+    /**
      * When set, the selection is verified against malicious arguments.
      * When using this class to create a statement using
      * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
@@ -214,6 +248,14 @@
     }
 
     /**
+     * Get if the query is marked as strict, as last configured by
+     * {@link #setStrict(boolean)}.
+     */
+    public boolean getStrict() {
+        return mStrict;
+    }
+
+    /**
      * Build an SQL query string from the given clauses.
      *
      * @param distinct true if you want each row to be unique, false otherwise.
@@ -222,21 +264,21 @@
      *            return all columns, which is discouraged to prevent reading
      *            data from storage that isn't going to be used.
      * @param where A filter declaring which rows to return, formatted as an SQL
-     *            WHERE clause (excluding the WHERE itself). Passing null will
+     *            {@code WHERE} clause (excluding the {@code WHERE} itself). Passing {@code null} will
      *            return all rows for the given URL.
      * @param groupBy A filter declaring how to group rows, formatted as an SQL
-     *            GROUP BY clause (excluding the GROUP BY itself). Passing null
+     *            {@code GROUP BY} clause (excluding the {@code GROUP BY} itself). Passing {@code null}
      *            will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in the cursor,
-     *            if row grouping is being used, formatted as an SQL HAVING
-     *            clause (excluding the HAVING itself). Passing null will cause
+     *            if row grouping is being used, formatted as an SQL {@code HAVING}
+     *            clause (excluding the {@code HAVING} itself). Passing null will cause
      *            all row groups to be included, and is required when row
      *            grouping is not being used.
-     * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
-     *            (excluding the ORDER BY itself). Passing null will use the
+     * @param orderBy How to order the rows, formatted as an SQL {@code ORDER BY} clause
+     *            (excluding the {@code ORDER BY} itself). Passing null will use the
      *            default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
-     *            formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+     *            formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause.
      * @return the SQL query string
      */
     public static String buildQueryString(
@@ -308,22 +350,22 @@
      *   null will return all columns, which is discouraged to prevent
      *   reading data from storage that isn't going to be used.
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself). Passing null will return all rows for the given URL.
      * @param selectionArgs You may include ?s in selection, which
      *   will be replaced by the values from selectionArgs, in order
      *   that they appear in the selection. The values will be bound
      *   as Strings.
      * @param groupBy A filter declaring how to group rows, formatted
-     *   as an SQL GROUP BY clause (excluding the GROUP BY
+     *   as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY}
      *   itself). Passing null will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in
      *   the cursor, if row grouping is being used, formatted as an
-     *   SQL HAVING clause (excluding the HAVING itself).  Passing
+     *   SQL {@code HAVING} clause (excluding the {@code HAVING} itself).  Passing
      *   null will cause all row groups to be included, and is
      *   required when row grouping is not being used.
      * @param sortOrder How to order the rows, formatted as an SQL
-     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
+     *   {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null
      *   will use the default sort order, which may be unordered.
      * @return a cursor over the result set
      * @see android.content.ContentResolver#query(android.net.Uri, String[],
@@ -345,25 +387,25 @@
      *   null will return all columns, which is discouraged to prevent
      *   reading data from storage that isn't going to be used.
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself). Passing null will return all rows for the given URL.
      * @param selectionArgs You may include ?s in selection, which
      *   will be replaced by the values from selectionArgs, in order
      *   that they appear in the selection. The values will be bound
      *   as Strings.
      * @param groupBy A filter declaring how to group rows, formatted
-     *   as an SQL GROUP BY clause (excluding the GROUP BY
+     *   as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY}
      *   itself). Passing null will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in
      *   the cursor, if row grouping is being used, formatted as an
-     *   SQL HAVING clause (excluding the HAVING itself).  Passing
+     *   SQL {@code HAVING} clause (excluding the {@code HAVING} itself).  Passing
      *   null will cause all row groups to be included, and is
      *   required when row grouping is not being used.
      * @param sortOrder How to order the rows, formatted as an SQL
-     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
+     *   {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null
      *   will use the default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
-     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+     *   formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause.
      * @return a cursor over the result set
      * @see android.content.ContentResolver#query(android.net.Uri, String[],
      *      String, String[], String)
@@ -384,25 +426,25 @@
      *   null will return all columns, which is discouraged to prevent
      *   reading data from storage that isn't going to be used.
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself). Passing null will return all rows for the given URL.
      * @param selectionArgs You may include ?s in selection, which
      *   will be replaced by the values from selectionArgs, in order
      *   that they appear in the selection. The values will be bound
      *   as Strings.
      * @param groupBy A filter declaring how to group rows, formatted
-     *   as an SQL GROUP BY clause (excluding the GROUP BY
+     *   as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY}
      *   itself). Passing null will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in
      *   the cursor, if row grouping is being used, formatted as an
-     *   SQL HAVING clause (excluding the HAVING itself).  Passing
+     *   SQL {@code HAVING} clause (excluding the {@code HAVING} itself).  Passing
      *   null will cause all row groups to be included, and is
      *   required when row grouping is not being used.
      * @param sortOrder How to order the rows, formatted as an SQL
-     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
+     *   {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null
      *   will use the default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
-     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+     *   formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause.
      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
@@ -467,7 +509,7 @@
      *
      * @param db the database to update on
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself). Passing null will return all rows for the given URL.
      * @param selectionArgs You may include ?s in selection, which
      *   will be replaced by the values from selectionArgs, in order
@@ -537,7 +579,7 @@
      *
      * @param db the database to delete on
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself). Passing null will return all rows for the given URL.
      * @param selectionArgs You may include ?s in selection, which
      *   will be replaced by the values from selectionArgs, in order
@@ -589,8 +631,8 @@
     }
 
     /**
-     * Construct a SELECT statement suitable for use in a group of
-     * SELECT statements that will be joined through UNION operators
+     * Construct a {@code SELECT} statement suitable for use in a group of
+     * {@code SELECT} statements that will be joined through {@code UNION} operators
      * in buildUnionQuery.
      *
      * @param projectionIn A list of which columns to return. Passing
@@ -598,23 +640,23 @@
      *    prevent reading data from storage that isn't going to be
      *    used.
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself).  Passing null will return all rows for the given
      *   URL.
      * @param groupBy A filter declaring how to group rows, formatted
-     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
+     *   as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} itself).
      *   Passing null will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in
      *   the cursor, if row grouping is being used, formatted as an
-     *   SQL HAVING clause (excluding the HAVING itself).  Passing
+     *   SQL {@code HAVING} clause (excluding the {@code HAVING} itself).  Passing
      *   null will cause all row groups to be included, and is
      *   required when row grouping is not being used.
      * @param sortOrder How to order the rows, formatted as an SQL
-     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
+     *   {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null
      *   will use the default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
-     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
-     * @return the resulting SQL SELECT statement
+     *   formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause.
+     * @return the resulting SQL {@code SELECT} statement
      */
     public String buildQuery(
             String[] projectionIn, String selection, String groupBy,
@@ -677,8 +719,8 @@
     }
 
     /**
-     * Construct a SELECT statement suitable for use in a group of
-     * SELECT statements that will be joined through UNION operators
+     * Construct a {@code SELECT} statement suitable for use in a group of
+     * {@code SELECT} statements that will be joined through {@code UNION} operators
      * in buildUnionQuery.
      *
      * @param typeDiscriminatorColumn the name of the result column
@@ -686,8 +728,8 @@
      *   each row was drawn.
      * @param unionColumns the names of the columns to appear in the
      *   result.  This may include columns that do not appear in the
-     *   table this SELECT is querying (i.e. mTables), but that do
-     *   appear in one of the other tables in the UNION query that we
+     *   table this {@code SELECT} is querying (i.e. mTables), but that do
+     *   appear in one of the other tables in the {@code UNION} query that we
      *   are constructing.
      * @param columnsPresentInTable a Set of the names of the columns
      *   that appear in this table (i.e. in the table whose name is
@@ -702,18 +744,18 @@
      * @param typeDiscriminatorValue the value used for the
      *   type-discriminator column in this subquery
      * @param selection A filter declaring which rows to return,
-     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE}
      *   itself).  Passing null will return all rows for the given
      *   URL.
      * @param groupBy A filter declaring how to group rows, formatted
-     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
+     *   as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} itself).
      *   Passing null will cause the rows to not be grouped.
      * @param having A filter declare which row groups to include in
      *   the cursor, if row grouping is being used, formatted as an
-     *   SQL HAVING clause (excluding the HAVING itself).  Passing
+     *   SQL {@code HAVING} clause (excluding the {@code HAVING} itself).  Passing
      *   null will cause all row groups to be included, and is
      *   required when row grouping is not being used.
-     * @return the resulting SQL SELECT statement
+     * @return the resulting SQL {@code SELECT} statement
      */
     public String buildUnionSubQuery(
             String typeDiscriminatorColumn,
@@ -771,18 +813,18 @@
     }
 
     /**
-     * Given a set of subqueries, all of which are SELECT statements,
+     * Given a set of subqueries, all of which are {@code SELECT} statements,
      * construct a query that returns the union of what those
      * subqueries return.
-     * @param subQueries an array of SQL SELECT statements, all of
+     * @param subQueries an array of SQL {@code SELECT} statements, all of
      *   which must have the same columns as the same positions in
      *   their results
      * @param sortOrder How to order the rows, formatted as an SQL
-     *   ORDER BY clause (excluding the ORDER BY itself).  Passing
+     *   {@code ORDER BY} clause (excluding the {@code ORDER BY} itself).  Passing
      *   null will use the default sort order, which may be unordered.
      * @param limit The limit clause, which applies to the entire union result set
      *
-     * @return the resulting SQL SELECT statement
+     * @return the resulting SQL {@code SELECT} statement
      */
     public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
         StringBuilder query = new StringBuilder(128);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d257c03..a696eeb 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -156,21 +156,21 @@
     }
 
     /**
-     * Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+     * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
      *
      * @param token an opaque token returned by password confirmation.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void resetTimeout(byte[] token) {
+    public void resetLockout(byte[] token) {
         if (mService != null) {
             try {
-                mService.resetTimeout(token);
+                mService.resetLockout(token);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         } else {
-            Slog.w(TAG, "resetTimeout(): Service not connected");
+            Slog.w(TAG, "resetLockout(): Service not connected");
         }
     }
 
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index a20e2bf..4971911 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -49,8 +49,8 @@
     // Client lifecycle is still managed in <Biometric>Service.
     void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
 
-    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetTimeout(in byte [] token);
+    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetLockout(in byte [] token);
 
     // TODO(b/123378871): Remove when moved.
     // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index f413d7c..0c07a67 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -65,7 +65,8 @@
     @SystemApi
     public static final int CAPABILITY_NONE = 0x0;
     /**
-     * The device can properly apply transforms over protected content.
+     * The device can use GPU composition on protected content (layers whose buffers are protected
+     * in the trusted memory zone).
      *
      * @hide
      */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 44b653c..8a0a9c7 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -563,7 +563,8 @@
      * 0 produces a grayscale image, 1 is normal.
      *
      * @hide
-     * @deprecated use {@link ColorDisplayManager#setSaturationLevel(int)}.
+     * @deprecated use {@link ColorDisplayManager#setSaturationLevel(int)} instead. The level passed
+     * as a parameter here will be rounded to the nearest hundredth.
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f3ebd7f..ac44fe9 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -16,6 +16,8 @@
 
 package android.hardware.display;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -229,7 +231,17 @@
         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
 
-    public void registerDisplayListener(DisplayListener listener, Handler handler) {
+    /**
+     * Register a listener for display-related changes.
+     *
+     * @param listener The listener that will be called when display changes occur.
+     * @param handler Handler for the thread that will be receiving the callbacks. May be null.
+     * If null, listener will use the handler for the current thread, and if still null,
+     * the handler for the main thread.
+     * If that is still null, a runtime exception will be thrown.
+     */
+    public void registerDisplayListener(@NonNull DisplayListener listener,
+            @Nullable Handler handler) {
         if (listener == null) {
             throw new IllegalArgumentException("listener must not be null");
         }
@@ -237,7 +249,8 @@
         synchronized (mLock) {
             int index = findDisplayListenerLocked(listener);
             if (index < 0) {
-                mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
+                Looper looper = getLooperForHandler(handler);
+                mDisplayListeners.add(new DisplayListenerDelegate(listener, looper));
                 registerCallbackIfNeededLocked();
             }
         }
@@ -258,6 +271,17 @@
         }
     }
 
+    private static Looper getLooperForHandler(@Nullable Handler handler) {
+        Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
+        if (looper == null) {
+            looper = Looper.getMainLooper();
+        }
+        if (looper == null) {
+            throw new RuntimeException("Could not get Looper for the UI thread.");
+        }
+        return looper;
+    }
+
     private int findDisplayListenerLocked(DisplayListener listener) {
         final int numListeners = mDisplayListeners.size();
         for (int i = 0; i < numListeners; i++) {
@@ -636,8 +660,8 @@
     private static final class DisplayListenerDelegate extends Handler {
         public final DisplayListener mListener;
 
-        public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
-            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
+        DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) {
+            super(looper, null, true /*async*/);
             mListener = listener;
         }
 
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index efe24e5..55b340f 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -107,6 +107,11 @@
             mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
                     new Face(null, faceId, deviceId)).sendToTarget();
         }
+
+        @Override
+        public void onEnumerated(long deviceId, int faceId, int remaining) {
+            // TODO: Finish. Low priority since it's not used.
+        }
     };
 
     /**
@@ -474,25 +479,6 @@
     }
 
     /**
-     * Reset the lockout timer when asked to do so by keyguard.
-     *
-     * @param token an opaque token returned by password confirmation.
-     * @hide
-     */
-    @RequiresPermission(MANAGE_BIOMETRIC)
-    public void resetTimeout(byte[] token) {
-        if (mService != null) {
-            try {
-                mService.resetTimeout(token);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            Log.w(TAG, "resetTimeout(): Service not connected!");
-        }
-    }
-
-    /**
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f67760a..9609e99 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -86,8 +86,8 @@
     // Gets the authenticator ID for face
     long getAuthenticatorId(String opPackageName);
 
-    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetTimeout(in byte [] cryptoToken);
+    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetLockout(in byte [] token);
 
     // Add a callback which gets notified when the face lockout period expired.
     void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index b88574b..cec9fd8 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -28,4 +28,5 @@
     void onAuthenticationFailed(long deviceId);
     void onError(long deviceId, int error, int vendorCode);
     void onRemoved(long deviceId, int faceId, int remaining);
+    void onEnumerated(long deviceId, int faceId, int remaining);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 80d404d..d0622c8 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,7 +18,6 @@
 
 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;
 
@@ -724,26 +723,6 @@
     }
 
     /**
-     * Reset the lockout timer when asked to do so by keyguard.
-     *
-     * @param token an opaque token returned by password confirmation.
-     *
-     * @hide
-     */
-    @RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
-    public void resetTimeout(byte[] token) {
-        if (mService != null) {
-            try {
-                mService.resetTimeout(token);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            Slog.w(TAG, "resetTimeout(): Service not connected!");
-        }
-    }
-
-    /**
      * @hide
      */
     public void addLockoutResetCallback(final LockoutResetCallback callback) {
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 97868fa..64448fd 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -16,7 +16,6 @@
 
 package android.hardware.input;
 
-import android.app.IInputForwarder;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.IInputDevicesChangedListener;
@@ -82,7 +81,4 @@
     void setCustomPointerIcon(in PointerIcon icon);
 
     void requestPointerCapture(IBinder windowToken, boolean enabled);
-
-    /** Create input forwarder to deliver touch events to owned display. */
-    IInputForwarder createInputForwarder(int displayId);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index bfb7c58..fec5c34 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -21,7 +21,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
-import android.app.IInputForwarder;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.os.Binder;
@@ -934,26 +933,6 @@
         }
     }
 
-
-    /**
-     * Create an {@link IInputForwarder} targeted to provided display.
-     * {@link android.Manifest.permission.INJECT_EVENTS} permission is required to call this method.
-     *
-     * @param displayId Id of the target display where input events should be forwarded.
-     *                  Display must exist and must be owned by the caller.
-     * @return The forwarder instance.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public IInputForwarder createInputForwarder(int displayId) {
-        try {
-            return mIm.createInputForwarder(displayId);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
     private void populateInputDevicesLocked() {
         if (mInputDevicesChangedListener == null) {
             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2aca55a..6d195ae 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -678,11 +678,20 @@
     @Deprecated
     public static final int TYPE_VPN = 17;
 
-    /** {@hide} */
-    public static final int MAX_RADIO_TYPE   = TYPE_VPN;
+    /**
+     * A network that is exclusively meant to be used for testing
+     *
+     * @deprecated Use {@link NetworkCapabilities} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
 
     /** {@hide} */
-    public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+    public static final int MAX_RADIO_TYPE = TYPE_TEST;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_TEST;
 
     private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
 
@@ -3927,15 +3936,16 @@
      *
      * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
      * corresponding permission.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Extras to include in the app start intent.
      * @hide
      */
     @SystemApi
     @TestApi
     @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
-    public void startCaptivePortalApp(Bundle appExtras) {
+    public void startCaptivePortalApp(Network network, Bundle appExtras) {
         try {
-            mService.startCaptivePortalAppInternal(appExtras);
+            mService.startCaptivePortalAppInternal(network, appExtras);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 872671f..87c62d2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -168,7 +168,7 @@
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
     void startCaptivePortalApp(in Network network);
-    void startCaptivePortalAppInternal(in Bundle appExtras);
+    void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
 
     boolean getAvoidBadWifi();
     int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index 41f969a..c94cdde 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -34,6 +34,7 @@
 
     void start();
     void launchCaptivePortalApp();
+    void notifyCaptivePortalAppFinished(int response);
     void forceReevaluation(int uid);
     void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
     void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index 5146585..2c61511 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -26,5 +26,4 @@
     void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
     void showProvisioningNotification(String action, String packageName);
     void hideProvisioningNotification();
-    void logCaptivePortalLoginEvent(int eventId, String packageName);
 }
\ No newline at end of file
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 62cf7d7..b9d49c1 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -36,8 +36,9 @@
     private LinkAddress mAddr;
     private HashSet<String> mFlags = Sets.newHashSet();
 
-    private static final String FLAG_UP = INetd.IF_STATE_UP;
-    private static final String FLAG_DOWN = INetd.IF_STATE_DOWN;
+    // Must be kept in sync with constant in INetd.aidl
+    private static final String FLAG_UP = "up";
+    private static final String FLAG_DOWN = "down";
 
     private static final  String[] EMPTY_STRING_ARRAY = new String[0];
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda1..1d2d81d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@
             TRANSPORT_VPN,
             TRANSPORT_WIFI_AWARE,
             TRANSPORT_LOWPAN,
+            TRANSPORT_TEST,
     })
     public @interface Transport { }
 
@@ -635,10 +636,18 @@
      */
     public static final int TRANSPORT_LOWPAN = 6;
 
+    /**
+     * Indicates this network uses a Test-only virtual interface as a transport.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int TRANSPORT_TEST = 7;
+
     /** @hide */
     public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
     /** @hide */
-    public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+    public static final int MAX_TRANSPORT = TRANSPORT_TEST;
 
     /** @hide */
     public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@
         "ETHERNET",
         "VPN",
         "WIFI_AWARE",
-        "LOWPAN"
+        "LOWPAN",
+        "TEST"
     };
 
     /**
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 5ab34e9..bf27262 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,16 +92,6 @@
     public static final int MASK_ALL_NETWORKS     = 0b11110000;
 
     public static final int FIREWALL_RULE_DEFAULT = 0;
-    public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW;
-    public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY;
-
-    public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST;
-    public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST;
-
-    public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE;
-    public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE;
-    public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY;
-    public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE;
 
     public static final String FIREWALL_CHAIN_NAME_NONE = "none";
     public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 7a4c9bc..dbb894f 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,44 +15,17 @@
  */
 package android.net;
 
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
 
 /**
- * Service used to communicate with the network stack, which is running in a separate module.
+ *
+ * Constants for client code communicating with the network stack service.
  * @hide
  */
-@SystemService(Context.NETWORK_STACK_SERVICE)
 @SystemApi
 @TestApi
 public class NetworkStack {
-    private static final String TAG = NetworkStack.class.getSimpleName();
-
     /**
      * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
      * protection level.
@@ -63,209 +36,5 @@
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
-    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-
-    @NonNull
-    @GuardedBy("mPendingNetStackRequests")
-    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
-    @Nullable
-    @GuardedBy("mPendingNetStackRequests")
-    private INetworkStackConnector mConnector;
-
-    private volatile boolean mNetworkStackStartRequested = false;
-
-    private interface NetworkStackCallback {
-        void onNetworkStackConnected(INetworkStackConnector connector);
-    }
-
-    /** @hide */
-    public NetworkStack() { }
-
-    /**
-     * Create a DHCP server according to the specified parameters.
-     *
-     * <p>The server will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
-            final IDhcpServerCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeDhcpServer(ifName, params, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create an IpClient on the specified interface.
-     *
-     * <p>The IpClient will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeIpClient(ifName, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create a NetworkMonitor.
-     *
-     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeNetworkMonitor(
-            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeNetworkMonitor(network, name, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    private class NetworkStackConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            registerNetworkStackService(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // TODO: crash/reboot the system ?
-            Slog.wtf(TAG, "Lost network stack connector");
-        }
-    };
-
-    private void registerNetworkStackService(@NonNull IBinder service) {
-        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
-
-        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
-                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-
-        final ArrayList<NetworkStackCallback> requests;
-        synchronized (mPendingNetStackRequests) {
-            requests = new ArrayList<>(mPendingNetStackRequests);
-            mPendingNetStackRequests.clear();
-            mConnector = connector;
-        }
-
-        for (NetworkStackCallback r : requests) {
-            r.onNetworkStackConnected(connector);
-        }
-    }
-
-    /**
-     * Start the network stack. Should be called only once on device startup.
-     *
-     * <p>This method will start the network stack either in the network stack process, or inside
-     * the system server on devices that do not support the network stack module. The network stack
-     * connector will then be delivered asynchronously to clients that requested it before it was
-     * started.
-     * @hide
-     */
-    public void start(Context context) {
-        mNetworkStackStartRequested = true;
-        // Try to bind in-process if the library is available
-        IBinder connector = null;
-        try {
-            final Class service = Class.forName(
-                    "com.android.server.NetworkStackService",
-                    true /* initialize */,
-                    context.getClassLoader());
-            connector = (IBinder) service.getMethod("makeConnector", Context.class)
-                    .invoke(null, context);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
-            // TODO: crash/reboot system here ?
-            return;
-        } catch (ClassNotFoundException e) {
-            // Normal behavior if stack is provided by the app: fall through
-        }
-
-        // In-process network stack. Add the service to the service manager here.
-        if (connector != null) {
-            registerNetworkStackService(connector);
-            return;
-        }
-        // Start the network stack process. The service will be added to the service manager in
-        // NetworkStackConnection.onServiceConnected().
-        final Intent intent = new Intent(INetworkStackConnector.class.getName());
-        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
-        intent.setComponent(comp);
-
-        if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(),
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            Slog.wtf(TAG,
-                    "Could not bind to network stack in-process, or in app with " + intent);
-            // TODO: crash/reboot system server if no network stack after a timeout ?
-        }
-    }
-
-    /**
-     * For non-system server clients, get the connector registered by the system server.
-     */
-    private INetworkStackConnector getRemoteConnector() {
-        // Block until the NetworkStack connector is registered in ServiceManager.
-        // <p>This is only useful for non-system processes that do not have a way to be notified of
-        // registration completion. Adding a callback system would be too heavy weight considering
-        // that the connector is registered on boot, so it is unlikely that a client would request
-        // it before it is registered.
-        // TODO: consider blocking boot on registration and simplify much of the logic in this class
-        IBinder connector;
-        try {
-            final long before = System.currentTimeMillis();
-            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
-                Thread.sleep(20);
-                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
-                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
-                    return null;
-                }
-            }
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
-            return null;
-        }
-
-        return INetworkStackConnector.Stub.asInterface(connector);
-    }
-
-    private void requestConnector(@NonNull NetworkStackCallback request) {
-        // TODO: PID check.
-        final int caller = Binder.getCallingUid();
-        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
-            // Don't even attempt to obtain the connector and give a nice error message
-            throw new SecurityException(
-                    "Only the system server should try to bind to the network stack.");
-        }
-
-        if (!mNetworkStackStartRequested) {
-            // The network stack is not being started in this process, e.g. this process is not
-            // the system server. Get a remote connector registered by the system server.
-            final INetworkStackConnector connector = getRemoteConnector();
-            synchronized (mPendingNetStackRequests) {
-                mConnector = connector;
-            }
-            request.onNetworkStackConnected(connector);
-            return;
-        }
-
-        final INetworkStackConnector connector;
-        synchronized (mPendingNetStackRequests) {
-            connector = mConnector;
-            if (connector == null) {
-                mPendingNetStackRequests.add(request);
-                return;
-            }
-        }
-
-        request.onNetworkStackConnected(connector);
-    }
+    private NetworkStack() {}
 }
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 1f33693..a851e04 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -21,13 +21,14 @@
 import android.annotation.WorkerThread;
 
 import java.util.ArrayDeque;
-import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -190,13 +191,19 @@
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
 
-    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-    // We want at least 2 threads and at most 4 threads in the core pool,
-    // preferring to have 1 less than the CPU count to avoid saturating
-    // the CPU with background work
-    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
-    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
-    private static final int KEEP_ALIVE_SECONDS = 30;
+    // We keep only a single pool thread around all the time.
+    // We let the pool grow to a fairly large number of threads if necessary,
+    // but let them time out quickly. In the unlikely case that we run out of threads,
+    // we fall back to a simple unbounded-queue executor.
+    // This combination ensures that:
+    // 1. We normally keep few threads (1) around.
+    // 2. We queue only after launching a significantly larger, but still bounded, set of threads.
+    // 3. We keep the total number of threads bounded, but still allow an unbounded set
+    //    of tasks to be queued.
+    private static final int CORE_POOL_SIZE = 1;
+    private static final int MAXIMUM_POOL_SIZE = 20;
+    private static final int BACKUP_POOL_SIZE = 5;
+    private static final int KEEP_ALIVE_SECONDS = 3;
 
     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger(1);
@@ -206,8 +213,29 @@
         }
     };
 
-    private static final BlockingQueue<Runnable> sPoolWorkQueue =
-            new LinkedBlockingQueue<Runnable>(128);
+    // Used only for rejected executions.
+    // Initialization protected by sRunOnSerialPolicy lock.
+    private static ThreadPoolExecutor sBackupExecutor;
+    private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue;
+
+    private static final RejectedExecutionHandler sRunOnSerialPolicy =
+            new RejectedExecutionHandler() {
+        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+            android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
+            // As a last ditch fallback, run it on an executor with an unbounded queue.
+            // Create this executor lazily, hopefully almost never.
+            synchronized (this) {
+                if (sBackupExecutor == null) {
+                    sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
+                    sBackupExecutor = new ThreadPoolExecutor(
+                            BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
+                            TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
+                    sBackupExecutor.allowCoreThreadTimeOut(true);
+                }
+            }
+            sBackupExecutor.execute(r);
+        }
+    };
 
     /**
      * An {@link Executor} that can be used to execute tasks in parallel.
@@ -217,8 +245,8 @@
     static {
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
-                sPoolWorkQueue, sThreadFactory);
-        threadPoolExecutor.allowCoreThreadTimeOut(true);
+                new SynchronousQueue<Runnable>(), sThreadFactory);
+        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
         THREAD_POOL_EXECUTOR = threadPoolExecutor;
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index ab6dd7c..b7e65b9 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -46,6 +46,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -262,6 +263,7 @@
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
     private static final long BYTES_PER_GB = 1073741824; //1024^3
+    public static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
 
     private static final String VERSION_DATA = "vers";
     private static final String UID_DATA = "uid";
@@ -482,6 +484,13 @@
          * yield a value of 0 if the device doesn't support power calculations.
          */
         public abstract LongCounter getPowerCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing total power monitored on the rails
+         * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device
+         * doesn't support power rail monitoring.
+         */
+        public abstract LongCounter getMonitoredRailChargeConsumedMaMs();
     }
 
     /**
@@ -1526,6 +1535,9 @@
         // The charge of the battery in micro-Ampere-hours.
         public int batteryChargeUAh;
 
+        public double modemRailChargeMah;
+        public double wifiRailChargeMah;
+
         // Constants from SCREEN_BRIGHTNESS_*
         public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_BRIGHTNESS_MASK = 0x7;
@@ -1738,6 +1750,8 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(batteryChargeUAh);
+            dest.writeDouble(modemRailChargeMah);
+            dest.writeDouble(wifiRailChargeMah);
             dest.writeInt(states);
             dest.writeInt(states2);
             if (wakelockTag != null) {
@@ -1767,6 +1781,8 @@
             batteryTemperature = (short)(bat2&0xffff);
             batteryVoltage = (char)((bat2>>16)&0xffff);
             batteryChargeUAh = src.readInt();
+            modemRailChargeMah = src.readDouble();
+            wifiRailChargeMah = src.readDouble();
             states = src.readInt();
             states2 = src.readInt();
             if ((bat&0x10000000) != 0) {
@@ -1807,6 +1823,8 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             batteryChargeUAh = 0;
+            modemRailChargeMah = 0;
+            wifiRailChargeMah = 0;
             states = 0;
             states2 = 0;
             wakelockTag = null;
@@ -1835,6 +1853,8 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             batteryChargeUAh = o.batteryChargeUAh;
+            modemRailChargeMah = o.modemRailChargeMah;
+            wifiRailChargeMah = o.wifiRailChargeMah;
             states = o.states;
             states2 = o.states2;
             if (o.wakelockTag != null) {
@@ -1867,6 +1887,8 @@
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
                     && batteryChargeUAh == o.batteryChargeUAh
+                    && modemRailChargeMah == o.modemRailChargeMah
+                    && wifiRailChargeMah == o.wifiRailChargeMah
                     && states == o.states
                     && states2 == o.states2
                     && currentTime == o.currentTime;
@@ -3311,7 +3333,8 @@
 
         if (counter.getIdleTimeCounter().getCountLocked(which) != 0
                 || counter.getRxTimeCounter().getCountLocked(which) != 0
-                || counter.getPowerCounter().getCountLocked(which) != 0) {
+                || counter.getPowerCounter().getCountLocked(which) != 0
+                || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) {
             return true;
         }
 
@@ -3345,7 +3368,10 @@
         pw.print(",");
         pw.print(counter.getRxTimeCounter().getCountLocked(which));
         pw.print(",");
-        pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+        pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        pw.print(",");
+        pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                / (MILLISECONDS_IN_HOUR));
         for (LongCounter c : counter.getTxTimeCounters()) {
             pw.print(",");
             pw.print(c.getCountLocked(which));
@@ -3370,7 +3396,10 @@
         proto.write(ControllerActivityProto.RX_DURATION_MS,
                 counter.getRxTimeCounter().getCountLocked(which));
         proto.write(ControllerActivityProto.POWER_MAH,
-                counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+                counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH,
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                        / (MILLISECONDS_IN_HOUR));
 
         long tToken;
         LongCounter[] txCounters = counter.getTxTimeCounters();
@@ -3400,6 +3429,8 @@
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
         final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
         // Battery real time
         final long totalControllerActivityTimeMs
             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
@@ -3522,10 +3553,22 @@
             sb.append("     ");
             sb.append(controllerName);
             sb.append(" Battery drain: ").append(
-                BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60)));
+                    BatteryStatsHelper.makemAh(powerDrainMaMs / MILLISECONDS_IN_HOUR));
             sb.append("mAh");
             pw.println(sb.toString());
         }
+
+        if (monitoredRailChargeConsumedMaMs > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Monitored rail energy drain: ").append(
+                    new DecimalFormat("#.##").format(
+                            monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
     }
 
     /**
@@ -6103,6 +6146,8 @@
         int oldTemp = -1;
         int oldVolt = -1;
         int oldChargeMAh = -1;
+        double oldModemRailChargeMah = -1;
+        double oldWifiRailChargeMah = -1;
         long lastTime = -1;
 
         void reset() {
@@ -6114,6 +6159,8 @@
             oldTemp = -1;
             oldVolt = -1;
             oldChargeMAh = -1;
+            oldModemRailChargeMah = -1;
+            oldWifiRailChargeMah = -1;
         }
 
         public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
@@ -6299,6 +6346,16 @@
                     item.append(checkin ? ",Bcc=" : " charge=");
                     item.append(oldChargeMAh);
                 }
+                if (oldModemRailChargeMah != rec.modemRailChargeMah) {
+                    oldModemRailChargeMah = rec.modemRailChargeMah;
+                    item.append(checkin ? ",Mrc=" : " modemRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah));
+                }
+                if (oldWifiRailChargeMah != rec.wifiRailChargeMah) {
+                    oldWifiRailChargeMah = rec.wifiRailChargeMah;
+                    item.append(checkin ? ",Wrc=" : " wifiRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah));
+                }
                 printBitDescriptions(item, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
                 printBitDescriptions(item, oldState2, rec.states2, null,
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index aba81af..149ef54 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2439,4 +2439,11 @@
             VMDebug.attachAgent(library + "=" + options, classLoader);
         }
     }
+
+    /**
+     * Return the current free ZRAM usage in kilobytes.
+     *
+     * @hide
+     */
+    public static native long getZramFreeKb();
 }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3feccf5..0aed981 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -388,7 +388,7 @@
 
     /** {@hide} */
     public static File getDataStagingDirectory(String volumeUuid) {
-        return new File(getDataDirectory(volumeUuid), "staging");
+        return new File(getDataDirectory(volumeUuid), "pkg_staging");
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0384faa..316572c 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -85,7 +85,7 @@
 /**
  * Utility methods useful for working with files.
  */
-public class FileUtils {
+public final class FileUtils {
     private static final String TAG = "FileUtils";
 
     /** {@hide} */ public static final int S_IRWXU = 00700;
@@ -309,6 +309,7 @@
      * kernel before falling back to a userspace copy as a last resort.
      *
      * @return number of bytes copied.
+     * @hide
      */
     public static long copy(@NonNull File from, @NonNull File to) throws IOException {
         return copy(from, to, null, null, null);
@@ -324,6 +325,7 @@
      * @param executor that listener events should be delivered via.
      * @param listener to be periodically notified as the copy progresses.
      * @return number of bytes copied.
+     * @hide
      */
     public static long copy(@NonNull File from, @NonNull File to,
             @Nullable CancellationSignal signal, @Nullable Executor executor,
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 269c781..4a14ece 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -24,14 +25,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
-import android.gamedriver.GameDriverProto.Blacklist;
-import android.gamedriver.GameDriverProto.Blacklists;
-import android.opengl.EGL14;
 import android.provider.Settings;
-import android.util.Base64;
 import android.util.Log;
-
-import com.android.framework.protobuf.InvalidProtocolBufferException;
+import android.widget.Toast;
 
 import dalvik.system.VMRuntime;
 
@@ -64,11 +60,11 @@
     private static final String SYSTEM_DRIVER_VERSION_NAME = "";
     private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+    private static final String PROPERTY_GFX_DRIVER_BUILD_DATE = "ro.gfx.driver.build_date";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
-    private static final String GAME_DRIVER_BLACKLIST_FLAG = "blacklist";
-    private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
+    private static final String GAME_DRIVER_WHITELIST_ALL = "*";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -83,8 +79,9 @@
         setupGpuLayers(context, coreSettings, pm, packageName);
         setupAngle(context, coreSettings, pm, packageName);
         if (!chooseDriver(context, coreSettings, pm, packageName)) {
+            final String driverBuildDate = SystemProperties.get(PROPERTY_GFX_DRIVER_BUILD_DATE);
             setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
-                    packageName);
+                    driverBuildDate == null ? "" : driverBuildDate, packageName);
         }
     }
 
@@ -222,9 +219,17 @@
     }
 
 
-    private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) {
-        List<String> valueList = null;
-        final String settingsValue = bundle.getString(globalSetting);
+    private static List<String> getGlobalSettingsString(ContentResolver contentResolver,
+                                                        Bundle bundle,
+                                                        String globalSetting) {
+        final List<String> valueList;
+        final String settingsValue;
+
+        if (bundle != null) {
+            settingsValue = bundle.getString(globalSetting);
+        } else {
+            settingsValue = Settings.Global.getString(contentResolver, globalSetting);
+        }
 
         if (settingsValue != null) {
             valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
@@ -246,17 +251,27 @@
         return -1;
     }
 
-    private static String getDriverForPkg(Bundle bundle, String packageName) {
-        final String allUseAngle =
-                bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+    private static String getDriverForPkg(Context context, Bundle bundle, String packageName) {
+        final String allUseAngle;
+        if (bundle != null) {
+            allUseAngle =
+                    bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+        } else {
+            ContentResolver contentResolver = context.getContentResolver();
+            allUseAngle = Settings.Global.getString(contentResolver,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+        }
         if ((allUseAngle != null) && allUseAngle.equals("1")) {
             return sDriverMap.get(OpenGlDriverChoice.ANGLE);
         }
 
-        final List<String> globalSettingsDriverPkgs = getGlobalSettingsString(
-                bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
-        final List<String> globalSettingsDriverValues = getGlobalSettingsString(
-                bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
+        final ContentResolver contentResolver = context.getContentResolver();
+        final List<String> globalSettingsDriverPkgs =
+                getGlobalSettingsString(contentResolver, bundle,
+                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
+        final List<String> globalSettingsDriverValues =
+                getGlobalSettingsString(contentResolver, bundle,
+                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
 
         // Make sure we have a good package name
         if ((packageName == null) || (packageName.isEmpty())) {
@@ -308,7 +323,7 @@
      * True: Temporary rules file was loaded.
      * False: Temporary rules file was *not* loaded.
      */
-    private boolean setupAngleWithTempRulesFile(Context context,
+    private static boolean setupAngleWithTempRulesFile(Context context,
                                                 String packageName,
                                                 String paths,
                                                 String devOptIn) {
@@ -372,7 +387,7 @@
      * True: APK rules file was loaded.
      * False: APK rules file was *not* loaded.
      */
-    private boolean setupAngleRulesApk(String anglePkgName,
+    private static boolean setupAngleRulesApk(String anglePkgName,
             ApplicationInfo angleInfo,
             PackageManager pm,
             String packageName,
@@ -405,23 +420,32 @@
     /**
      * Pull ANGLE whitelist from GlobalSettings and compare against current package
      */
-    private boolean checkAngleWhitelist(Bundle bundle, String packageName) {
+    private static boolean checkAngleWhitelist(Context context, Bundle bundle, String packageName) {
+        final ContentResolver contentResolver = context.getContentResolver();
         final List<String> angleWhitelist =
-                getGlobalSettingsString(bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST);
+                getGlobalSettingsString(contentResolver, bundle,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST);
 
         return angleWhitelist.contains(packageName);
     }
 
     /**
      * Pass ANGLE details down to trigger enable logic
+     *
+     * @param context
+     * @param bundle
+     * @param packageName
+     * @return true: ANGLE setup successfully
+     *         false: ANGLE not setup (not on whitelist, ANGLE not present, etc.)
      */
-    public void setupAngle(Context context, Bundle bundle, PackageManager pm, String packageName) {
+    public boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+            String packageName) {
         if (packageName.isEmpty()) {
             Log.v(TAG, "No package name available yet, skipping ANGLE setup");
-            return;
+            return false;
         }
 
-        final String devOptIn = getDriverForPkg(bundle, packageName);
+        final String devOptIn = getDriverForPkg(context, bundle, packageName);
         if (DEBUG) {
             Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
                     + "set to: '" + devOptIn + "'");
@@ -439,11 +463,11 @@
         // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before
         // and can confidently answer yes/no based on the previously set developer
         // option value.
-        final boolean whitelisted = checkAngleWhitelist(bundle, packageName);
+        final boolean whitelisted = checkAngleWhitelist(context, bundle, packageName);
         final boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT));
         final boolean rulesCheck = (whitelisted || !defaulted);
         if (!rulesCheck) {
-            return;
+            return false;
         }
 
         if (whitelisted) {
@@ -456,7 +480,7 @@
         final String anglePkgName = getAnglePackageName(pm);
         if (anglePkgName.isEmpty()) {
             Log.e(TAG, "Failed to find ANGLE package.");
-            return;
+            return false;
         }
 
         final ApplicationInfo angleInfo;
@@ -464,7 +488,7 @@
             angleInfo = pm.getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY);
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
-            return;
+            return false;
         }
 
         final String abi = chooseAbi(angleInfo);
@@ -480,12 +504,62 @@
 
         if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
             // We setup ANGLE with a temp rules file, so we're done here.
-            return;
+            return true;
         }
 
         if (setupAngleRulesApk(anglePkgName, angleInfo, pm, packageName, paths, devOptIn)) {
             // We setup ANGLE with rules from the APK, so we're done here.
-            return;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if the "ANGLE In Use" dialog box should be shown.
+     */
+    private boolean shouldShowAngleInUseDialogBox(Context context) {
+        try {
+            ContentResolver contentResolver = context.getContentResolver();
+            final int showDialogBox = Settings.Global.getInt(contentResolver,
+                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX);
+
+            return (showDialogBox == 1);
+        } catch (Settings.SettingNotFoundException | SecurityException e) {
+            // Do nothing and move on
+        }
+
+        // No setting, so assume false
+        return false;
+    }
+
+    /**
+     * Determine if ANGLE should be used.
+     */
+    private boolean shouldUseAngle(Context context, String packageName) {
+        // Need to make sure we are evaluating ANGLE usage for the correct circumstances
+        if (!setupAngle(context, null, context.getPackageManager(), packageName)) {
+            Log.v(TAG, "Package '" + packageName + "' should use not ANGLE");
+            return false;
+        }
+
+        final boolean useAngle = getShouldUseAngle(packageName);
+        Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'");
+
+        return useAngle;
+    }
+
+    /**
+     * Show the ANGLE in Use Dialog Box
+     * @param context
+     */
+    public void showAngleInUseDialogBox(Context context) {
+        final String packageName = context.getPackageName();
+
+        if (shouldShowAngleInUseDialogBox(context) && shouldUseAngle(context, packageName)) {
+            final String toastMsg = packageName + " is using ANGLE";
+            final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG);
+            toast.show();
         }
     }
 
@@ -501,8 +575,8 @@
 
         final PackageInfo driverPackageInfo;
         try {
-            driverPackageInfo =
-                    pm.getPackageInfo(driverPackageName, PackageManager.MATCH_SYSTEM_ONLY);
+            driverPackageInfo = pm.getPackageInfo(driverPackageName,
+                    PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
             return false;
@@ -541,53 +615,33 @@
 
         if (gameDriverAllApps != 1) {
             // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS
-            if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS)
-                            .contains(packageName)) {
+            if (getGlobalSettingsString(null, coreSettings,
+                    Settings.Global.GAME_DRIVER_OPT_OUT_APPS).contains(packageName)) {
                 if (DEBUG) {
                     Log.w(TAG, packageName + " opts out from Game Driver.");
                 }
                 return false;
             }
             final boolean isOptIn =
-                    getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS)
-                            .contains(packageName);
-            if (!isOptIn
-                    && !getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_WHITELIST)
-                                .contains(packageName)) {
+                    getGlobalSettingsString(null, coreSettings,
+                            Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName);
+            final List<String> whitelist = getGlobalSettingsString(null, coreSettings,
+                    Settings.Global.GAME_DRIVER_WHITELIST);
+            if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0
+                    && !whitelist.contains(packageName)) {
                 if (DEBUG) {
                     Log.w(TAG, packageName + " is not on the whitelist.");
                 }
                 return false;
             }
 
-            if (!isOptIn) {
-                // At this point, the application is on the whitelist only, check whether it's
-                // on the blacklist, terminate early when it's on the blacklist.
-                try {
-                    // TODO(b/121350991) Switch to DeviceConfig with property listener.
-                    final String base64String =
-                            coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST);
-                    if (base64String != null && !base64String.isEmpty()) {
-                        final Blacklists blacklistsProto =
-                                Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
-                        final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList();
-                        final long driverVersionCode = driverAppInfo.longVersionCode;
-                        for (Blacklist blacklist : blacklists) {
-                            if (blacklist.getVersionCode() == driverVersionCode) {
-                                for (String pkgName : blacklist.getPackageNamesList()) {
-                                    if (pkgName == packageName) {
-                                        return false;
-                                    }
-                                }
-                                break;
-                            }
-                        }
-                    }
-                } catch (InvalidProtocolBufferException e) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Can't parse blacklist, skip and continue...");
-                    }
-                }
+            // If the application is not opted-in and check whether it's on the blacklist,
+            // terminate early if it's on the blacklist and fallback to system driver.
+            if (!isOptIn
+                    && getGlobalSettingsString(null, coreSettings,
+                                               Settings.Global.GAME_DRIVER_BLACKLIST)
+                            .contains(ai.packageName)) {
+                return false;
             }
         }
 
@@ -602,9 +656,6 @@
             return false;
         }
 
-        setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode,
-                packageName);
-
         final StringBuilder sb = new StringBuilder();
         sb.append(driverAppInfo.nativeLibraryDir)
           .append(File.pathSeparator);
@@ -616,26 +667,13 @@
         if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
         setDriverPath(paths);
 
-        return true;
-    }
+        final String driverBuildDate = driverAppInfo.metaData == null
+                ? ""
+                : driverAppInfo.metaData.getString("driver_build_date");
+        setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode,
+                driverBuildDate == null ? "" : driverBuildDate, packageName);
 
-    /**
-     * Start a background thread to initialize EGL.
-     *
-     * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
-     * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
-     * its first frame adds directly to user-visible app launch latency. By starting it earlier
-     * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
-     *
-     * Should only be called after chooseDriver().
-     */
-    public static void earlyInitEGL() {
-        final Thread eglInitThread = new Thread(
-                () -> {
-                    EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
-                },
-                "EGL Init");
-        eglInitThread.start();
+        return true;
     }
 
     private static String chooseAbi(ApplicationInfo ai) {
@@ -657,7 +695,8 @@
     private static native void setDebugLayersGLES(String layers);
     private static native void setDriverPath(String path);
     private static native void setGpuStats(String driverPackageName, String driverVersionName,
-            long driverVersionCode, String appPackageName);
+            long driverVersionCode, String driverBuildDate, String appPackageName);
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
             FileDescriptor rulesFd, long rulesOffset, long rulesLength);
+    private static native boolean getShouldUseAngle(String packageName);
 }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index bdef575..483c41a 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -74,4 +74,7 @@
 
     // controls whether PowerManager should doze after the screen turns off or not
     void setDozeAfterScreenOff(boolean on);
+
+    // Forces the system to suspend even if there are held wakelocks.
+    boolean forceSuspend();
 }
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index dde46cd..0751b96 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -69,6 +69,12 @@
     oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs);
 
     /**
+     * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list
+     * of active configs has changed.
+     */
+    oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds);
+
+    /**
      * Requests StatsCompanionService to send a broadcast using the given intentSender
      * (which should cast to an IIntentSender), along with the other information specified.
      */
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index f1bba1a..6d4c5a0 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -196,4 +196,25 @@
     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
     */
    oneway void unregisterPullerCallback(int atomTag, String packageName);
+
+    /**
+     * The install requires staging.
+     */
+    const int FLAG_REQUIRE_STAGING = 0x01;
+
+    /**
+     * Rollback is enabled with this install.
+     */
+    const int FLAG_ROLLBACK_ENABLED = 0x02;
+
+    /**
+     * Requires low latency monitoring.
+     */
+    const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
+
+    /**
+     * Logs an event for binary push for module updates.
+     */
+     oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode,
+         in int options, in int state, in long[] experimentId);
 }
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 87e1b7d..1420e2f 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.SystemApi;
 import android.content.LocaleProto;
 import android.icu.util.ULocale;
 import android.util.proto.ProtoOutputStream;
@@ -324,6 +325,15 @@
         return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
     }
 
+    /**
+     * Returns true if locale is a pseudo-locale, false otherwise.
+     * {@hide}
+     */
+    @SystemApi
+    public static boolean isPseudoLocale(@Nullable ULocale locale) {
+        return isPseudoLocale(locale != null ? locale.toLocale() : null);
+    }
+
     @IntRange(from=0, to=1)
     private static int matchScore(Locale supported, Locale desired) {
         if (supported.equals(desired)) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2ecf9d1..6bd1f2b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -363,10 +363,15 @@
     public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1;
 
     /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MIN = 0;
+
+    /**
      * Go to sleep reason code: Going to sleep due by application request.
      * @hide
      */
-    public static final int GO_TO_SLEEP_REASON_APPLICATION = 0;
+    public static final int GO_TO_SLEEP_REASON_APPLICATION = GO_TO_SLEEP_REASON_MIN;
 
     /**
      * Go to sleep reason code: Going to sleep due by request of the
@@ -412,6 +417,17 @@
     public static final int GO_TO_SLEEP_REASON_ACCESSIBILITY = 7;
 
     /**
+     * Go to sleep reason code: Going to sleep due to force-suspend.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_FORCE_SUSPEND;
+
+    /**
      * @hide
      */
     public static String sleepReasonToString(int sleepReason) {
@@ -424,6 +440,7 @@
             case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
             case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
             case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
+            case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
             default: return Integer.toString(sleepReason);
         }
     }
@@ -1784,7 +1801,7 @@
      *
      * see {@link #registerThermalStatusCallback}
      */
-    public void unregisterThermalStatusCallback(ThermalStatusCallback callback) {
+    public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) {
         Preconditions.checkNotNull(callback, "callback cannnot be null");
         synchronized (this) {
             if (mThermalService == null) {
@@ -1853,6 +1870,32 @@
     }
 
     /**
+     * Forces the device to go to suspend, even if there are currently wakelocks being held.
+     * <b>Caution</b>
+     * This is a very dangerous command as it puts the device to sleep immediately. Apps and parts
+     * of the system will not be notified and will not have an opportunity to save state prior to
+     * the device going to suspend.
+     * This method should only be used in very rare circumstances where the device is intended
+     * to appear as completely off to the user and they have a well understood, reliable way of
+     * re-enabling it.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @return true on success, false otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public boolean forceSuspend() {
+        try {
+            return mService.forceSuspend();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
      * This broadcast is only sent to registered receivers.
      */
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8492b0c..3ee54ce 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,7 @@
 import android.provider.Settings;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
+import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.Display;
 import android.view.WindowManager;
@@ -762,7 +763,8 @@
 
         String reasonArg = null;
         if (!TextUtils.isEmpty(reason)) {
-            reasonArg = "--reason=" + sanitizeArg(reason);
+            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
         }
 
         final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ee3d354..1de8117 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -21,6 +21,7 @@
 import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.provider.DeviceConfig;
 import android.util.Log;
 import android.util.Slog;
 
@@ -66,16 +67,6 @@
     /**
      * @hide for internal use only.
      */
-    public static final String ZYGOTE_SOCKET_NAME = "zygote";
-
-    /**
-     * @hide for internal use only.
-     */
-    public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary";
-
-    /**
-     * @hide for internal use only.
-     */
     public static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
 
     /**
@@ -89,19 +80,14 @@
     /**
      * @hide for internal use only
      */
-    public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool";
-
-    /**
-     * @hide for internal use only
-     */
-    public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary";
-
-    /**
-     * @hide for internal use only
-     */
     private static final String LOG_TAG = "ZygoteProcess";
 
     /**
+     * The default value for enabling the blastula pool.
+     */
+    private static final String BLASTULA_POOL_ENABLED_DEFAULT = "false";
+
+    /**
      * The name of the socket used to communicate with the primary zygote.
      */
     private final LocalSocketAddress mZygoteSocketAddress;
@@ -110,6 +96,7 @@
      * The name of the secondary (alternate ABI) zygote socket.
      */
     private final LocalSocketAddress mZygoteSecondarySocketAddress;
+
     /**
      * The name of the socket used to communicate with the primary blastula pool.
      */
@@ -122,17 +109,21 @@
 
     public ZygoteProcess() {
         mZygoteSocketAddress =
-                new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
+                new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
+                                       LocalSocketAddress.Namespace.RESERVED);
         mZygoteSecondarySocketAddress =
-                new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME,
+                new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME,
                                        LocalSocketAddress.Namespace.RESERVED);
 
         mBlastulaPoolSocketAddress =
-                new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME,
+                new LocalSocketAddress(Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME,
                                        LocalSocketAddress.Namespace.RESERVED);
         mBlastulaPoolSecondarySocketAddress =
-                new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME,
+                new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME,
                                        LocalSocketAddress.Namespace.RESERVED);
+
+        // TODO (chriswailes): Uncomment when the blastula pool can be enabled.
+//        fetchBlastulaPoolEnabledProp();
     }
 
     public ZygoteProcess(LocalSocketAddress primarySocketAddress,
@@ -272,6 +263,15 @@
     private ZygoteState secondaryZygoteState;
 
     /**
+     * If the blastula pool should be created and used to start applications.
+     *
+     * Setting this value to false will disable the creation, maintenance, and use of the blastula
+     * pool.  When the blastula pool is disabled the application lifecycle will be identical to
+     * previous versions of Android.
+     */
+    private boolean mBlastulaPoolEnabled = false;
+
+    /**
      * Start a new process.
      *
      * <p>If processes are enabled, a new process is created and the
@@ -327,6 +327,14 @@
                                                   @Nullable String sandboxId,
                                                   boolean useBlastulaPool,
                                                   @Nullable String[] zygoteArgs) {
+        if (fetchBlastulaPoolEnabledProp()) {
+            // TODO (chriswailes): Send the appropriate command to the zygotes
+            Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled);
+
+            // This can't be enabled yet, but we do want to test this code path.
+            mBlastulaPoolEnabled = false;
+        }
+
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
@@ -407,7 +415,7 @@
         Process.ProcessStartResult result = new Process.ProcessStartResult();
 
         // TODO (chriswailes): Move branch body into separate function.
-        if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) {
+        if (useBlastulaPool && isValidBlastulaCommand(args)) {
             LocalSocket blastulaSessionSocket = null;
 
             try {
@@ -620,6 +628,7 @@
             final StringBuilder sb = new StringBuilder();
             sb.append("--packages-for-uid=");
 
+            // TODO (chriswailes): Replace with String.join
             for (int i = 0; i < packagesForUid.length; ++i) {
                 if (i != 0) {
                     sb.append(',');
@@ -656,11 +665,42 @@
 
         synchronized(mLock) {
             return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
-                                              useBlastulaPool,
+                                              useBlastulaPool && mBlastulaPoolEnabled,
                                               argsForZygote);
         }
     }
 
+    private boolean fetchBlastulaPoolEnabledProp() {
+        boolean origVal = mBlastulaPoolEnabled;
+
+        final String propertyString =
+                Zygote.getSystemProperty(
+                        DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED,
+                        BLASTULA_POOL_ENABLED_DEFAULT);
+
+        if (!propertyString.isEmpty()) {
+            mBlastulaPoolEnabled =
+                    Zygote.getSystemPropertyBoolean(
+                            DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED,
+                            Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT));
+        }
+
+        return origVal != mBlastulaPoolEnabled;
+    }
+
+    private long mLastPropCheckTimestamp = 0;
+
+    private boolean fetchBlastulaPoolEnabledPropWithMinInterval() {
+        final long currentTimestamp = SystemClock.elapsedRealtime();
+
+        if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) {
+            mLastPropCheckTimestamp = currentTimestamp;
+            return fetchBlastulaPoolEnabledProp();
+        }
+
+        return false;
+    }
+
     /**
      * Closes the connections to the zygote, if they exist.
      */
@@ -940,7 +980,7 @@
 
     /**
      * Try connecting to the Zygote over and over again until we hit a time-out.
-     * @param socketName The name of the socket to connect to.
+     * @param zygoteSocketName The name of the socket to connect to.
      */
     public static void waitForConnectionToZygote(String zygoteSocketName) {
         final LocalSocketAddress zygoteSocketAddress =
@@ -950,7 +990,7 @@
 
     /**
      * Try connecting to the Zygote over and over again until we hit a time-out.
-     * @param address The name of the socket to connect to.
+     * @param zygoteSocketAddress The name of the socket to connect to.
      */
     public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) {
         int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS;
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 2593c85..c99ecb32 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -44,6 +44,7 @@
   private long[] mTimeInRatMs;
   private long[] mTimeInRxSignalStrengthLevelMs;
   private long[] mTxTimeMs;
+  private long mMonitoredRailChargeConsumedMaMs;
 
   public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new
       Parcelable.Creator<CellularBatteryStats>() {
@@ -74,6 +75,7 @@
     out.writeLongArray(mTimeInRatMs);
     out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
     out.writeLongArray(mTxTimeMs);
+    out.writeLong(mMonitoredRailChargeConsumedMaMs);
   }
 
   public void readFromParcel(Parcel in) {
@@ -90,6 +92,7 @@
     in.readLongArray(mTimeInRatMs);
     in.readLongArray(mTimeInRxSignalStrengthLevelMs);
     in.readLongArray(mTxTimeMs);
+    mMonitoredRailChargeConsumedMaMs = in.readLong();
   }
 
   public long getLoggingDurationMs() {
@@ -144,6 +147,10 @@
     return mTxTimeMs;
   }
 
+  public long getMonitoredRailChargeConsumedMaMs() {
+    return mMonitoredRailChargeConsumedMaMs;
+  }
+
   public void setLoggingDurationMs(long t) {
     mLoggingDurationMs = t;
     return;
@@ -211,6 +218,11 @@
     return;
   }
 
+  public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+    mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+    return;
+  }
+
   public int describeContents() {
     return 0;
   }
@@ -237,6 +249,7 @@
     Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
     mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS];
     Arrays.fill(mTxTimeMs, 0);
+    mMonitoredRailChargeConsumedMaMs = 0;
     return;
   }
 }
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
index e5341ee..3639c71 100644
--- a/core/java/android/os/connectivity/WifiBatteryStats.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -44,6 +44,7 @@
   private long[] mTimeInStateMs;
   private long[] mTimeInSupplicantStateMs;
   private long[] mTimeInRxSignalStrengthLevelMs;
+  private long mMonitoredRailChargeConsumedMaMs;
 
   public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
       Parcelable.Creator<WifiBatteryStats>() {
@@ -77,6 +78,7 @@
     out.writeLongArray(mTimeInStateMs);
     out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
     out.writeLongArray(mTimeInSupplicantStateMs);
+    out.writeLong(mMonitoredRailChargeConsumedMaMs);
   }
 
   public void readFromParcel(Parcel in) {
@@ -96,6 +98,7 @@
     in.readLongArray(mTimeInStateMs);
     in.readLongArray(mTimeInRxSignalStrengthLevelMs);
     in.readLongArray(mTimeInSupplicantStateMs);
+    mMonitoredRailChargeConsumedMaMs = in.readLong();
   }
 
   public long getLoggingDurationMs() {
@@ -162,6 +165,10 @@
     return mTimeInSupplicantStateMs;
   }
 
+  public long getMonitoredRailChargeConsumedMaMs() {
+    return mMonitoredRailChargeConsumedMaMs;
+  }
+
   public void setLoggingDurationMs(long t) {
     mLoggingDurationMs = t;
     return;
@@ -245,6 +252,11 @@
     return;
   }
 
+  public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+    mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+    return;
+  }
+
   public int describeContents() {
     return 0;
   }
@@ -274,6 +286,7 @@
     Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
     mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
     Arrays.fill(mTimeInSupplicantStateMs, 0);
+    mMonitoredRailChargeConsumedMaMs = 0;
     return;
   }
 }
\ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 735f4f2..90a5f76 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -38,6 +38,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.ObbInfo;
 import android.content.res.ObbScanner;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -55,6 +56,7 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
+import android.provider.MediaStore;
 import android.provider.Settings;
 import android.sysprop.VoldProperties;
 import android.system.ErrnoException;
@@ -1096,12 +1098,32 @@
     }
 
     /**
-     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
+     * Return the {@link StorageVolume} that contains the given file, or
+     * {@code null} if none.
      */
     public @Nullable StorageVolume getStorageVolume(File file) {
         return getStorageVolume(getVolumeList(), file);
     }
 
+    /**
+     * Return the {@link StorageVolume} that contains the given
+     * {@link MediaStore} item.
+     */
+    public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) {
+        final String volumeName = MediaStore.getVolumeName(uri);
+        switch (volumeName) {
+            case MediaStore.VOLUME_EXTERNAL:
+                return getPrimaryStorageVolume();
+            default:
+                for (StorageVolume vol : getStorageVolumes()) {
+                    if (Objects.equals(vol.getNormalizedUuid(), volumeName)) {
+                        return vol;
+                    }
+                }
+        }
+        throw new IllegalStateException("Unknown volume for " + uri);
+    }
+
     /** {@hide} */
     public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
         return getStorageVolume(getVolumeList(userId, 0), file);
@@ -1539,13 +1561,19 @@
         return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
     }
 
-    /** {@hide} */
+    /**
+     * Return if the currently booted device has the "isolated storage" feature
+     * flag enabled. This will eventually be fully enabled in the final
+     * {@link android.os.Build.VERSION_CODES#Q} release.
+     *
+     * @hide
+     */
     @SystemApi
     @TestApi
     public static boolean hasIsolatedStorage() {
         // Prefer to use snapshot for current boot when available
         return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
-                SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false));
+                SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true));
     }
 
     /**
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 89967c3..d62bc6c5 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -51,6 +51,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
@@ -85,9 +86,11 @@
 
     private static final Object sLock = new Object();
 
-    /** App global remote service used by all {@link PermissionControllerManager managers} */
+    /**
+     * Global remote services (per user) used by all {@link PermissionControllerManager managers}
+     */
     @GuardedBy("sLock")
-    private static RemoteService sRemoteService;
+    private static SparseArray<RemoteService> sRemoteServices = new SparseArray<>(1);
 
     /**
      * The key for retrieving the result from the returned bundle.
@@ -203,6 +206,7 @@
     }
 
     private final @NonNull Context mContext;
+    private final @NonNull RemoteService mRemoteService;
 
     /**
      * Create a new {@link PermissionControllerManager}.
@@ -213,14 +217,18 @@
      */
     public PermissionControllerManager(@NonNull Context context) {
         synchronized (sLock) {
-            if (sRemoteService == null) {
+            RemoteService remoteService = sRemoteServices.get(context.getUserId(), null);
+            if (remoteService == null) {
                 Intent intent = new Intent(SERVICE_INTERFACE);
                 intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
                 ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
 
-                sRemoteService = new RemoteService(context.getApplicationContext(),
-                        serviceInfo.getComponentInfo().getComponentName());
+                remoteService = new RemoteService(context.getApplicationContext(),
+                        serviceInfo.getComponentInfo().getComponentName(), context.getUser());
+                sRemoteServices.put(context.getUserId(), remoteService);
             }
+
+            mRemoteService = remoteService;
         }
 
         mContext = context;
@@ -255,7 +263,7 @@
                     + " required");
         }
 
-        sRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(sRemoteService,
+        mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService,
                 request, doDryRun, reason, mContext.getPackageName(), executor, callback));
     }
 
@@ -276,7 +284,7 @@
         checkNotNull(executor);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService,
+        mRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(mRemoteService,
                 user, executor, callback));
     }
 
@@ -294,8 +302,8 @@
         checkNotNull(backup);
         checkNotNull(user);
 
-        sRemoteService.scheduleAsyncRequest(
-                new PendingRestoreRuntimePermissionBackup(sRemoteService, backup, user));
+        mRemoteService.scheduleAsyncRequest(
+                new PendingRestoreRuntimePermissionBackup(mRemoteService, backup, user));
     }
 
     /**
@@ -318,8 +326,8 @@
         checkNotNull(executor);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(
-                new PendingRestoreDelayedRuntimePermissionBackup(sRemoteService, packageName,
+        mRemoteService.scheduleRequest(
+                new PendingRestoreDelayedRuntimePermissionBackup(mRemoteService, packageName,
                         user, executor, callback));
     }
 
@@ -338,8 +346,8 @@
         checkNotNull(packageName);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(sRemoteService,
-                packageName, callback, handler == null ? sRemoteService.getHandler() : handler));
+        mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
+                packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
     }
 
     /**
@@ -356,7 +364,7 @@
         checkNotNull(packageName);
         checkNotNull(permissionName);
 
-        sRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+        mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
                 permissionName));
     }
 
@@ -379,9 +387,9 @@
         checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(sRemoteService,
+        mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
                 permissionNames, flags, callback,
-                handler == null ? sRemoteService.getHandler() : handler));
+                handler == null ? mRemoteService.getHandler() : handler));
     }
 
     /**
@@ -402,7 +410,7 @@
         checkNotNull(executor);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService,
+        mRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(mRemoteService,
                 countSystem, numMillis, executor, callback));
     }
 
@@ -424,8 +432,8 @@
         checkNotNull(executor);
         checkNotNull(callback);
 
-        sRemoteService.scheduleRequest(new PendingIsApplicationQualifiedForRoleRequest(
-                sRemoteService, roleName, packageName, executor, callback));
+        mRemoteService.scheduleRequest(new PendingIsApplicationQualifiedForRoleRequest(
+                mRemoteService, roleName, packageName, executor, callback));
     }
 
     /**
@@ -441,11 +449,12 @@
          *
          * @param context A context to use
          * @param componentName The component of the service to connect to
+         * @param user User the remote service should be connected as
          */
-        RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
-            super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
-                    service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
-                    false, false, 1);
+        RemoteService(@NonNull Context context, @NonNull ComponentName componentName,
+                @NonNull UserHandle user) {
+            super(context, SERVICE_INTERFACE, componentName, user.getIdentifier(),
+                    service -> Log.e(TAG, "RemoteService " + service + " died"), false, false, 1);
         }
 
         /**
@@ -624,7 +633,7 @@
          *
          * <p>Needs to be called when canceling this task as it might be hung.
          */
-        void interruptRead() {
+        void interruptWrite() {
             IoUtils.closeQuietly(mLocalPipe);
         }
 
@@ -806,18 +815,19 @@
 
         @Override
         public void run(@NonNull IPermissionController service) {
+            mBackupSender.execute(mBackup);
+
             ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe();
             try {
                 service.restoreRuntimePermissionBackup(mUser, remotePipe);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error sending runtime permission backup", e);
                 mBackupSender.cancel(false);
+                mBackupSender.interruptWrite();
             } finally {
                 // Remote pipe end is duped by binder call. Local copy is not needed anymore
                 IoUtils.closeQuietly(remotePipe);
             }
-
-            mBackupSender.execute(mBackup);
         }
     }
 
diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java
new file mode 100644
index 0000000..92dbab3
--- /dev/null
+++ b/core/java/android/permission/PermissionManagerInternal.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ *
+ * <p>Only for use within the system server.
+ *
+ * @hide
+ */
+public abstract class PermissionManagerInternal {
+    /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user);
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-restoreable state will be delayed and can be
+     * re-tried via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    public abstract void restoreRuntimePermissions(@NonNull byte[] backup,
+            @NonNull UserHandle user);
+
+    /**
+     * Try to apply permission backup of a package that was previously not applied.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user);
+}
diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index f594c19..00c9e72 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -16,17 +16,18 @@
 
 package android.provider;
 
-public interface BaseColumns
-{
+import android.database.Cursor;
+
+public interface BaseColumns {
     /**
      * The unique ID for a row.
-     * <P>Type: INTEGER (long)</P>
      */
+    @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _ID = "_id";
 
     /**
      * The count of rows in a directory.
-     * <P>Type: INTEGER</P>
      */
+    // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _COUNT = "_count";
 }
diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java
new file mode 100644
index 0000000..1364fb8
--- /dev/null
+++ b/core/java/android/provider/Column.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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.provider;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a field is a {@link ContentProvider} column. It can be used as a
+ * key for {@link ContentValues} when inserting or updating data, or as a
+ * projection when querying.
+ *
+ * @hide
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface Column {
+    /**
+     * The {@link Cursor#getType(int)} of the data stored in this column.
+     */
+    int value();
+
+    /**
+     * This column is read-only and cannot be defined during insert or updates.
+     */
+    boolean readOnly() default false;
+}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 18e1f59..f6a8388 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -145,6 +145,38 @@
     @SystemApi
     public interface RuntimeNative {
         String NAMESPACE = "runtime_native";
+
+        /**
+         * Zygote flags. See {@link com.internal.os.Zygote}.
+         */
+
+        /**
+         * If {@code true}, enables the blastula pool feature.
+         *
+         * @hide for internal use only
+         */
+        String BLASTULA_POOL_ENABLED = "blastula_pool_enabled";
+
+        /**
+         * The maximum number of processes to keep in the blastula pool.
+         *
+         * @hide for internal use only
+         */
+        String BLASTULA_POOL_SIZE_MAX = "blastula_pool_size_max";
+
+        /**
+         * The minimum number of processes to keep in the blastula pool.
+         *
+         * @hide for internal use only
+         */
+        String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max";
+
+        /**
+         * The threshold used to determine if the pool should be refilled.
+         *
+         * @hide for internal use only
+         */
+        String BLASTULA_POOL_REFILL_THRESHOLD = "blastula_refill_threshold";
     }
 
     /**
@@ -308,6 +340,28 @@
     }
 
     /**
+     * Namespace for Rollback.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface Rollback {
+        String NAMESPACE = "rollback";
+
+        String BOOT_NAMESPACE = "rollback_boot";
+
+        /**
+         * Timeout in milliseconds for enabling package rollback.
+         */
+        String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
+
+       /**
+        * The lifetime duration of rollback packages in millis
+        */
+        String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
+    }
+
+    /**
      * Namespace for storage-related features.
      *
      * @hide
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 5f1c560..d282967 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -853,10 +853,8 @@
     private static final String PATH_DOCUMENT = "document";
     private static final String PATH_CHILDREN = "children";
     private static final String PATH_SEARCH = "search";
-    // TODO(b/72055774): make private again once ScopedAccessProvider is refactored
-    /** {@hide} */
     @UnsupportedAppUsage
-    public static final String PATH_TREE = "tree";
+    private static final String PATH_TREE = "tree";
 
     private static final String PARAM_QUERY = "query";
     private static final String PARAM_MANAGE = "manage";
@@ -1283,8 +1281,9 @@
      * @see DocumentsProvider#openDocumentThumbnail(String, Point,
      *      android.os.CancellationSignal)
      */
-    public static Bitmap getDocumentThumbnail(ContentInterface content, Uri documentUri, Point size,
-            CancellationSignal signal) throws FileNotFoundException {
+    public static @Nullable Bitmap getDocumentThumbnail(@NonNull ContentResolver content,
+            @NonNull Uri documentUri, @NonNull Point size, @Nullable CancellationSignal signal)
+            throws FileNotFoundException {
         try {
             return ContentResolver.loadThumbnail(content, documentUri, Point.convert(size), signal,
                     ImageDecoder.ALLOCATOR_SOFTWARE);
@@ -1297,12 +1296,6 @@
         }
     }
 
-    @Deprecated
-    public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
-            CancellationSignal signal) throws FileNotFoundException {
-        return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
-    }
-
     /**
      * Create a new document with given MIME type and display name.
      *
@@ -1311,8 +1304,9 @@
      * @param displayName name of new document
      * @return newly created document, or {@code null} if failed
      */
-    public static Uri createDocument(ContentInterface content, Uri parentDocumentUri,
-            String mimeType, String displayName) throws FileNotFoundException {
+    public static @Nullable Uri createDocument(@NonNull ContentResolver content,
+            @NonNull Uri parentDocumentUri, @NonNull String mimeType, @NonNull String displayName)
+            throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
@@ -1329,12 +1323,6 @@
         }
     }
 
-    @Deprecated
-    public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
-            String mimeType, String displayName) throws FileNotFoundException {
-        return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
-    }
-
     /**
      * Test if a document is descendant (child, grandchild, etc) from the given
      * parent.
@@ -1344,7 +1332,7 @@
      * @return if given document is a descendant of the given parent.
      * @see Root#FLAG_SUPPORTS_IS_CHILD
      */
-    public static boolean isChildDocument(@NonNull ContentInterface content,
+    public static boolean isChildDocument(@NonNull ContentResolver content,
             @NonNull Uri parentDocumentUri, @NonNull Uri childDocumentUri)
             throws FileNotFoundException {
         Preconditions.checkNotNull(content, "content can not be null");
@@ -1371,12 +1359,6 @@
         }
     }
 
-    @Deprecated
-    public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
-            Uri childDocumentUri) throws FileNotFoundException {
-        return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
-    }
-
     /**
      * Change the display name of an existing document.
      * <p>
@@ -1390,8 +1372,8 @@
      * @return the existing or new document after the rename, or {@code null} if
      *         failed.
      */
-    public static Uri renameDocument(ContentInterface content, Uri documentUri,
-            String displayName) throws FileNotFoundException {
+    public static @Nullable Uri renameDocument(@NonNull ContentResolver content,
+            @NonNull Uri documentUri, @NonNull String displayName) throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
@@ -1408,19 +1390,13 @@
         }
     }
 
-    @Deprecated
-    public static Uri renameDocument(ContentResolver content, Uri documentUri,
-            String displayName) throws FileNotFoundException {
-        return renameDocument((ContentInterface) content, documentUri, displayName);
-    }
-
     /**
      * Delete the given document.
      *
      * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
      * @return if the document was deleted successfully.
      */
-    public static boolean deleteDocument(ContentInterface content, Uri documentUri)
+    public static boolean deleteDocument(@NonNull ContentResolver content, @NonNull Uri documentUri)
             throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
@@ -1436,12 +1412,6 @@
         }
     }
 
-    @Deprecated
-    public static boolean deleteDocument(ContentResolver content, Uri documentUri)
-            throws FileNotFoundException {
-        return deleteDocument((ContentInterface) content, documentUri);
-    }
-
     /**
      * Copies the given document.
      *
@@ -1450,8 +1420,9 @@
      *         document's copy.
      * @return the copied document, or {@code null} if failed.
      */
-    public static Uri copyDocument(ContentInterface content, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) throws FileNotFoundException {
+    public static @Nullable Uri copyDocument(@NonNull ContentResolver content,
+            @NonNull Uri sourceDocumentUri, @NonNull Uri targetParentDocumentUri)
+            throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
@@ -1467,12 +1438,6 @@
         }
     }
 
-    @Deprecated
-    public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) throws FileNotFoundException {
-        return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
-    }
-
     /**
      * Moves the given document under a new parent.
      *
@@ -1482,8 +1447,9 @@
      *         document.
      * @return the moved document, or {@code null} if failed.
      */
-    public static Uri moveDocument(ContentInterface content, Uri sourceDocumentUri,
-            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
+    public static @Nullable Uri moveDocument(@NonNull ContentResolver content,
+            @NonNull Uri sourceDocumentUri, @NonNull Uri sourceParentDocumentUri,
+            @NonNull Uri targetParentDocumentUri) throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
@@ -1500,13 +1466,6 @@
         }
     }
 
-    @Deprecated
-    public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
-            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
-        return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
-                targetParentDocumentUri);
-    }
-
     /**
      * Removes the given document from a parent directory.
      *
@@ -1517,8 +1476,8 @@
      * @param parentDocumentUri parent document of the document to remove.
      * @return true if the document was removed successfully.
      */
-    public static boolean removeDocument(ContentInterface content, Uri documentUri,
-            Uri parentDocumentUri) throws FileNotFoundException {
+    public static boolean removeDocument(@NonNull ContentResolver content, @NonNull Uri documentUri,
+            @NonNull Uri parentDocumentUri) throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
@@ -1534,34 +1493,23 @@
         }
     }
 
-    @Deprecated
-    public static boolean removeDocument(ContentResolver content, Uri documentUri,
-            Uri parentDocumentUri) throws FileNotFoundException {
-        return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
-    }
-
     /**
      * Ejects the given root. It throws {@link IllegalStateException} when ejection failed.
      *
      * @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected
      */
-    public static void ejectRoot(ContentInterface content, Uri rootUri) {
+    public static void ejectRoot(@NonNull ContentResolver content, @NonNull Uri rootUri) {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
 
             content.call(rootUri.getAuthority(),
                     METHOD_EJECT_ROOT, null, in);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to eject", e);
         }
     }
 
-    @Deprecated
-    public static void ejectRoot(ContentResolver content, Uri rootUri) {
-        ejectRoot((ContentInterface) content, rootUri);
-    }
-
     /**
      * Returns metadata associated with the document. The type of metadata returned
      * is specific to the document type. For example the data returned for an image
@@ -1592,7 +1540,7 @@
      * @param documentUri a Document URI
      * @return a Bundle of Bundles.
      */
-    public static @Nullable Bundle getDocumentMetadata(@NonNull ContentInterface content,
+    public static @Nullable Bundle getDocumentMetadata(@NonNull ContentResolver content,
             @NonNull Uri documentUri) throws FileNotFoundException {
         Preconditions.checkNotNull(content, "content can not be null");
         Preconditions.checkNotNull(documentUri, "documentUri can not be null");
@@ -1609,12 +1557,6 @@
         }
     }
 
-    @Deprecated
-    public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
-            throws FileNotFoundException {
-        return getDocumentMetadata((ContentInterface) content, documentUri);
-    }
-
     /**
      * Finds the canonical path from the top of the document tree.
      *
@@ -1628,8 +1570,8 @@
      * @return the path of the document, or {@code null} if failed.
      * @see DocumentsProvider#findDocumentPath(String, String)
      */
-    public static Path findDocumentPath(ContentInterface content, Uri treeUri)
-            throws FileNotFoundException {
+    public static @Nullable Path findDocumentPath(@NonNull ContentResolver content,
+            @NonNull Uri treeUri) throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, treeUri);
@@ -1644,12 +1586,6 @@
         }
     }
 
-    @Deprecated
-    public static Path findDocumentPath(ContentResolver content, Uri treeUri)
-            throws FileNotFoundException {
-        return findDocumentPath((ContentInterface) content, treeUri);
-    }
-
     /**
      * Creates an intent for obtaining a web link for the specified document.
      *
@@ -1701,8 +1637,8 @@
      * @see DocumentsProvider#createWebLinkIntent(String, Bundle)
      * @see Intent#EXTRA_EMAIL
      */
-    public static IntentSender createWebLinkIntent(ContentInterface content, Uri uri,
-            Bundle options) throws FileNotFoundException {
+    public static @Nullable IntentSender createWebLinkIntent(@NonNull ContentResolver content,
+            @NonNull Uri uri, @Nullable Bundle options) throws FileNotFoundException {
         try {
             final Bundle in = new Bundle();
             in.putParcelable(DocumentsContract.EXTRA_URI, uri);
@@ -1723,12 +1659,6 @@
         }
     }
 
-    @Deprecated
-    public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
-            Bundle options) throws FileNotFoundException {
-        return createWebLinkIntent((ContentInterface) content, uri, options);
-    }
-
     /**
      * Open the given image for thumbnail purposes, using any embedded EXIF
      * thumbnail if available, and providing orientation hints from the parent
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0b38420..643307e 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -17,6 +17,8 @@
 package android.provider;
 
 import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.CurrentTimeSecondsLong;
 import android.annotation.DurationMillisLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -40,7 +42,9 @@
 import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
 import android.graphics.Point;
+import android.graphics.PostProcessor;
 import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
@@ -78,8 +82,15 @@
 import java.util.regex.Pattern;
 
 /**
- * The Media provider contains meta data for all available media on both internal
- * and external storage devices.
+ * The contract between the media provider and applications. Contains
+ * definitions for the supported URIs and columns.
+ * <p>
+ * The media provider provides an indexed collection of common media types, such
+ * as {@link Audio}, {@link Video}, and {@link Images}, from any attached
+ * storage devices. Each collection is organized based on the primary MIME type
+ * of the underlying content; for example, {@code image/*} content is indexed
+ * under {@link Images}. The {@link Files} collection provides a broad view
+ * across all collections, and does not filter by MIME type.
  */
 public final class MediaStore {
     private final static String TAG = "MediaStore";
@@ -847,18 +858,16 @@
     }
 
     /**
-     * Common fields for most MediaProvider tables
+     * Common media metadata columns.
      */
     public interface MediaColumns extends BaseColumns {
         /**
-         * Path to the file on disk.
+         * Path to the media item on disk.
          * <p>
          * Note that apps may not have filesystem permissions to directly access
          * this path. Instead of trying to open this path directly, apps should
          * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
          * access.
-         * <p>
-         * Type: TEXT
          *
          * @deprecated Apps may not have filesystem permissions to directly
          *             access this path. Instead of trying to open this path
@@ -869,10 +878,11 @@
          *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DATA = "_data";
 
         /**
-         * Hash of the file on disk.
+         * Hash of the media item on disk.
          * <p>
          * Contains a 20-byte binary blob which is the SHA-1 hash of the file as
          * persisted on disk. For performance reasons, the hash may not be
@@ -883,44 +893,44 @@
          * If you require the hash of a specific item, you can call
          * {@link ContentResolver#canonicalize(Uri)}, which will block until the
          * hash is calculated.
-         * <p>
-         * Type: BLOB
+         *
          * @removed
          */
         @Deprecated
+        @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
         public static final String HASH = "_hash";
 
         /**
-         * The size of the file in bytes
-         * <P>Type: INTEGER (long)</P>
+         * The size of the media item.
          */
+        @BytesLong
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String SIZE = "_size";
 
         /**
-         * The display name of the file
-         * <P>Type: TEXT</P>
+         * The display name of the media item.
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DISPLAY_NAME = "_display_name";
 
         /**
-         * The title of the content
-         * <P>Type: TEXT</P>
+         * The title of the media item.
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String TITLE = "title";
 
         /**
-         * The time the file was added to the media provider
-         * Units are seconds since 1970.
-         * <P>Type: INTEGER (long)</P>
+         * The time the media item was first added.
          */
+        @CurrentTimeSecondsLong
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_ADDED = "date_added";
 
         /**
-         * The time the file was last modified
-         * Units are seconds since 1970.
-         * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-         * <P>Type: INTEGER (long)</P>
+         * The time the media item was last modified.
          */
+        @CurrentTimeSecondsLong
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_MODIFIED = "date_modified";
 
         /**
@@ -938,9 +948,8 @@
          * {@code format} of {@code audio/ogg} would be ignored.
          * <p>
          * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String MIME_TYPE = "mime_type";
 
         /**
@@ -948,35 +957,34 @@
          * Used to pass the new file's object handle through the media scanner
          * from MTP to the media provider
          * For internal use only by MTP, media scanner and media provider.
-         * <P>Type: INTEGER</P>
          * @hide
          */
+        @Deprecated
+        // @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
 
         /**
          * Non-zero if the media file is drm-protected
-         * <P>Type: INTEGER (boolean)</P>
          * @hide
          */
         @UnsupportedAppUsage
+        @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_DRM = "is_drm";
 
         /**
          * Flag indicating if a media item is pending, and still being inserted
          * by its owner.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_PENDING
          * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
          */
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_PENDING = "is_pending";
 
         /**
          * Flag indicating if a media item is trashed.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_TRASHED
          * @see MediaStore#setIncludeTrashed(Uri)
@@ -985,57 +993,56 @@
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_TRASHED = "is_trashed";
 
         /**
-         * The time the file should be considered expired. Units are seconds
-         * since 1970. Typically only meaningful in the context of
-         * {@link #IS_PENDING} or {@link #IS_TRASHED}.
-         * <p>
-         * Type: INTEGER
+         * The time the media item should be considered expired. Typically only
+         * meaningful in the context of {@link #IS_PENDING} or
+         * {@link #IS_TRASHED}.
+         *
          * @removed
          */
         @Deprecated
+        @CurrentTimeSecondsLong
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String DATE_EXPIRES = "date_expires";
 
         /**
-         * The width of the image/video in pixels.
+         * The width of the media item, in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String WIDTH = "width";
 
         /**
-         * The height of the image/video in pixels.
+         * The height of the media item, in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String HEIGHT = "height";
 
         /**
          * Package name that contributed this media. The value may be
          * {@code NULL} if ownership cannot be reliably determined.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
 
         /**
          * The primary directory name this media exists under. The value may be
          * {@code NULL} if the media doesn't have a primary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setPrimaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String PRIMARY_DIRECTORY = "primary_directory";
 
         /**
          * The secondary directory name this media exists under. The value may
          * be {@code NULL} if the media doesn't have a secondary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setSecondaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String SECONDARY_DIRECTORY = "secondary_directory";
 
         /**
@@ -1046,11 +1053,8 @@
          * <p>
          * Each "document ID" is created once for each new resource. Different
          * renditions of that resource are expected to have different IDs.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String DOCUMENT_ID = "document_id";
 
         /**
@@ -1061,11 +1065,8 @@
          * <p>
          * This "instance ID" changes with each save operation of a specific
          * "document ID".
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String INSTANCE_ID = "instance_id";
 
         /**
@@ -1077,11 +1078,8 @@
          * For example, when you save a PSD document as a JPEG, then convert the
          * JPEG to GIF format, the "original document ID" of both the JPEG and
          * GIF files is the "document ID" of the original PSD file.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
     }
 
@@ -1166,49 +1164,62 @@
         }
 
         /**
-         * Fields for master table for all media files.
-         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
+         * File metadata columns.
          */
         public interface FileColumns extends MediaColumns {
             /**
              * The MTP storage ID of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String STORAGE_ID = "storage_id";
 
             /**
              * The MTP format code of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FORMAT = "format";
 
             /**
              * The index of the parent directory of the file
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String PARENT = "parent";
 
             /**
-             * The MIME type of the file
-             * <P>Type: TEXT</P>
+             * The MIME type of the media item.
+             * <p>
+             * This is typically defined based on the file extension of the media
+             * item. However, it may be the value of the {@code format} attribute
+             * defined by the <em>Dublin Core Media Initiative</em> standard,
+             * extracted from any XMP metadata contained within this media item.
+             * <p class="note">
+             * Note: the {@code format} attribute may be ignored if the top-level
+             * MIME type disagrees with the file extension. For example, it's
+             * reasonable for an {@code image/jpeg} file to declare a {@code format}
+             * of {@code image/vnd.google.panorama360+jpg}, but declaring a
+             * {@code format} of {@code audio/ogg} would be ignored.
+             * <p>
+             * This is a read-only column that is automatically computed.
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String MIME_TYPE = "mime_type";
 
             /**
-             * The title of the content
-             * <P>Type: TEXT</P>
+             * The title of the media item.
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE = "title";
 
             /**
              * The media type (audio, video, image or playlist)
              * of the file, or 0 for not a media file
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MEDIA_TYPE = "media_type";
 
             /**
@@ -1241,6 +1252,7 @@
              * Column indicating if the file is part of Downloads collection.
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_DOWNLOAD = "is_download";
         }
     }
@@ -1256,27 +1268,26 @@
         public static final Point MICRO_SIZE = new Point(96, 96);
     }
 
-    /** Column fields for downloaded files used in {@link Downloads} table */
+    /**
+     * Download metadata columns.
+     */
     public interface DownloadColumns extends MediaColumns {
         /**
-         * Uri indicating where the file has been downloaded from.
-         * <p>
-         * Type: TEXT
+         * Uri indicating where the item has been downloaded from.
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DOWNLOAD_URI = "download_uri";
 
         /**
          * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String REFERER_URI = "referer_uri";
 
         /**
          * The description of the download.
-         * <p>
-         * Type: Text
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DESCRIPTION = "description";
     }
 
@@ -1436,35 +1447,37 @@
     }
 
     /**
-     * Contains meta data for all available images.
+     * Collection of all media with MIME type of {@code image/*}.
      */
     public static final class Images {
+        /**
+         * Image metadata columns.
+         */
         public interface ImageColumns extends MediaColumns {
             /**
              * The description of the image
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * The picasa id of the image
-             * <P>Type: TEXT</P>
              *
              * @deprecated this value was only relevant for images hosted on
              *             Picasa, which are no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String PICASA_ID = "picasa_id";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The latitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1472,11 +1485,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1484,40 +1497,40 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
-             * The date & time that the image was taken in units
-             * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
+             * The time the media item was taken.
              */
+            @CurrentTimeMillisLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The orientation for the image expressed as degrees.
              * Only degrees 0, 90, 180, 270 will work.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ORIENTATION = "orientation";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -1525,9 +1538,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -1541,23 +1553,40 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
         }
 
         public static final class Media implements ImageColumns {
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
             }
 
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
                     String where, String orderBy) {
                 return cr.query(uri, projection, where,
                                              null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
             }
 
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
                     String selection, String [] selectionArgs, String orderBy) {
                 return cr.query(uri, projection, selection,
@@ -1569,9 +1598,12 @@
              *
              * @param cr The content resolver to use
              * @param url The url of the image
-             * @throws FileNotFoundException
-             * @throws IOException
+             * @deprecated loading of images should be performed through
+             *             {@link ImageDecoder#createSource(ContentResolver, Uri)},
+             *             which offers modern features like
+             *             {@link PostProcessor}.
              */
+            @Deprecated
             public static final Bitmap getBitmap(ContentResolver cr, Uri url)
                     throws FileNotFoundException, IOException {
                 InputStream input = cr.openInputStream(url);
@@ -1588,8 +1620,11 @@
              * @param name The name of the image
              * @param description The description of the image
              * @return The URL to the newly created image
-             * @throws FileNotFoundException
+             * @deprecated inserting of images should be performed through
+             *             {@link MediaStore#createPending(Context, PendingParams)},
+             *             which offers richer control over lifecycle.
              */
+            @Deprecated
             public static final String insertImage(ContentResolver cr, String imagePath,
                     String name, String description) throws FileNotFoundException {
                 // Check if file exists with a FileInputStream
@@ -1616,7 +1651,11 @@
              * @param description The description of the image
              * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
              *              for any reason.
+             * @deprecated inserting of images should be performed through
+             *             {@link MediaStore#createPending(Context, PendingParams)},
+             *             which offers richer control over lifecycle.
              */
+            @Deprecated
             public static final String insertImage(ContentResolver cr, Bitmap source,
                                                    String title, String description) {
                 ContentValues values = new ContentValues();
@@ -1711,15 +1750,33 @@
          */
         @Deprecated
         public static class Thumbnails implements BaseColumns {
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
             }
 
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
                     String[] projection) {
                 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
             }
 
+            /**
+             * @deprecated all queries should be performed through
+             *             {@link ContentResolver} directly, which offers modern
+             *             features like {@link CancellationSignal}.
+             */
+            @Deprecated
             public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
                     String[] projection) {
                 return cr.query(EXTERNAL_CONTENT_URI, projection,
@@ -1848,8 +1905,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -1860,128 +1915,137 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Images table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IMAGE_ID = "image_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
             public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
             public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
+
             /**
              * The blob raw data of thumbnail
-             * <P>Type: DATA STREAM</P>
+             *
+             * @deprecated this column never existed internally, and could never
+             *             have returned valid data.
              */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_BLOB)
             public static final String THUMB_DATA = "thumb_data";
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
 
     /**
-     * Container for all audio content.
+     * Collection of all media with MIME type of {@code audio/*}.
      */
     public static final class Audio {
         /**
-         * Columns for audio file that show up in multiple tables.
+         * Audio metadata columns.
          */
         public interface AudioColumns extends MediaColumns {
 
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
             /**
-             * The duration of the audio file, in ms
-             * <P>Type: INTEGER (long)</P>
+             * The duration of the audio item.
              */
+            @DurationMillisLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
-             * The position, in ms, playback was at when playback for this file
-             * was last stopped.
-             * <P>Type: INTEGER (long)</P>
+             * The position within the audio item at which playback should be
+             * resumed.
              */
+            @DurationMillisLong
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The id of the artist who created the audio file, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ARTIST_ID = "artist_id";
 
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The artist credited for the album that contains the audio file
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_ARTIST = "album_artist";
 
             /**
              * Whether the song is part of a compilation
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String COMPILATION = "compilation";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The composer of the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String COMPOSER = "composer";
 
             /**
              * The id of the album the audio file is from, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album the audio file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
@@ -1990,63 +2054,63 @@
              * disc number. For multi-disc sets, this number will
              * be 1xxx for tracks on the first disc, 2xxx for tracks
              * on the second disc, etc.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String TRACK = "track";
 
             /**
              * The year the audio file was recorded, if any
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String YEAR = "year";
 
             /**
              * Non-zero if the audio file is music
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_MUSIC = "is_music";
 
             /**
              * Non-zero if the audio file is a podcast
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_PODCAST = "is_podcast";
 
             /**
              * Non-zero if the audio file may be a ringtone
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_RINGTONE = "is_ringtone";
 
             /**
              * Non-zero if the audio file may be an alarm
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_ALARM = "is_alarm";
 
             /**
              * Non-zero if the audio file may be a notification sound
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_NOTIFICATION = "is_notification";
 
             /**
              * Non-zero if the audio file is an audiobook
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
              * The genre of the audio file, if any
-             * <P>Type: TEXT</P>
              * Does not exist in the database - only used by the media scanner for inserts.
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String GENRE = "genre";
 
             /**
              * The resource URI of a localized title, if any
-             * <P>Type: TEXT</P>
              * Conforms to this pattern:
              *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
              *   Authority: Package Name of ringtone title provider
@@ -2054,6 +2118,7 @@
              *   Second Path Segment: Resource ID of title
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
@@ -2142,6 +2207,7 @@
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path.
              */
+            @Deprecated
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
                 return getContentUri(getVolumeName(new File(path)));
             }
@@ -2197,13 +2263,13 @@
         }
 
         /**
-         * Columns representing an audio genre
+         * Audio genre metadata columns.
          */
         public interface GenresColumns {
             /**
              * The name of the genre
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
         }
 
@@ -2287,26 +2353,26 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the genre
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String GENRE_ID = "genre_id";
             }
         }
 
         /**
-         * Columns representing a playlist
+         * Audio playlist metadata columns.
          */
         public interface PlaylistsColumns {
             /**
              * The name of the playlist
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
 
             /**
@@ -2317,8 +2383,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2329,21 +2393,21 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
-             * The time the file was added to the media provider
-             * Units are seconds since 1970.
-             * <P>Type: INTEGER (long)</P>
+             * The time the media item was first added.
              */
+            @CurrentTimeSecondsLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_ADDED = "date_added";
 
             /**
-             * The time the file was last modified
-             * Units are seconds since 1970.
-             * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-             * <P>Type: INTEGER (long)</P>
+             * The time the media item was last modified.
              */
+            @CurrentTimeSecondsLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_MODIFIED = "date_modified";
         }
 
@@ -2426,6 +2490,7 @@
                 /**
                  * The ID within the playlist.
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String _ID = "_id";
 
                 /**
@@ -2436,20 +2501,20 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the playlist
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAYLIST_ID = "playlist_id";
 
                 /**
                  * The order of the songs in the playlist
-                 * <P>Type: INTEGER (long)></P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAY_ORDER = "play_order";
 
                 /**
@@ -2460,30 +2525,32 @@
         }
 
         /**
-         * Columns representing an artist
+         * Audio artist metadata columns.
          */
         public interface ArtistColumns {
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
         }
 
@@ -2545,40 +2612,40 @@
         }
 
         /**
-         * Columns representing an album
+         * Audio album metadata columns.
          */
         public interface AlbumColumns {
 
             /**
              * The id for the album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album on which the audio file appears, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The artist whose songs appear on this album
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The number of songs on this album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS = "numsongs";
 
             /**
              * This column is available when getting album info via artist,
              * and indicates the number of songs on the album by the given
              * artist.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
 
             /**
@@ -2586,8 +2653,8 @@
              * on this album were released. This will often
              * be the same as {@link #LAST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FIRST_YEAR = "minyear";
 
             /**
@@ -2595,20 +2662,19 @@
              * on this album were released. This will often
              * be the same as {@link #FIRST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String LAST_YEAR = "maxyear";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
              * Cached album art.
-             * <P>Type: TEXT</P>
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2619,6 +2685,7 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2676,8 +2743,48 @@
             // Not instantiable.
             private Radio() { }
         }
+
+        /**
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Audio} items.
+         *
+         * @deprecated Callers should migrate to using
+         *             {@link ContentResolver#loadThumbnail}, since it offers
+         *             richer control over requested thumbnail sizes and
+         *             cancellation behavior.
+         * @hide
+         */
+        @Deprecated
+        public static class Thumbnails implements BaseColumns {
+            /**
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
+             */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
+            public static final String DATA = "_data";
+
+            @Column(Cursor.FIELD_TYPE_INTEGER)
+            public static final String ALBUM_ID = "album_id";
+        }
     }
 
+    /**
+     * Collection of all media with MIME type of {@code video/*}.
+     */
     public static final class Video {
 
         /**
@@ -2685,69 +2792,78 @@
          */
         public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
 
+        /**
+         * @deprecated all queries should be performed through
+         *             {@link ContentResolver} directly, which offers modern
+         *             features like {@link CancellationSignal}.
+         */
+        @Deprecated
         public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
             return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
         }
 
+        /**
+         * Video metadata columns.
+         */
         public interface VideoColumns extends MediaColumns {
 
             /**
-             * The duration of the video file, in ms
-             * <P>Type: INTEGER (long)</P>
+             * The duration of the video item.
              */
+            @DurationMillisLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The artist who created the video file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The album the video file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The resolution of the video file, formatted as "XxY"
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String RESOLUTION = "resolution";
 
             /**
              * The description of the video recording
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The user-added tags associated with a video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String TAGS = "tags";
 
             /**
              * The YouTube category of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String CATEGORY = "category";
 
             /**
              * The language of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String LANGUAGE = "language";
 
             /**
              * The latitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2755,11 +2871,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2767,33 +2883,33 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
-             * The date & time that the video was taken in units
-             * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
+             * The time the media item was taken.
              */
+            @CurrentTimeMillisLong
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -2801,9 +2917,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -2817,39 +2932,37 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
 
             /**
-             * The bookmark for the video. Time in ms. Represents the location in the video that the
-             * video should start playing at the next time it is opened. If the value is null or
-             * out of the range 0..DURATION-1 then the video should start playing from the
-             * beginning.
-             * <P>Type: INTEGER</P>
+             * The position within the video item at which playback should be
+             * resumed.
              */
+            @DurationMillisLong
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The standard of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_STANDARD = "color_standard";
 
             /**
              * The transfer of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_TRANSFER = "color_transfer";
 
             /**
              * The range of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_RANGE = "color_range";
         }
 
@@ -3016,8 +3129,6 @@
 
             /**
              * Path to the thumbnail file on disk.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -3028,18 +3139,19 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Video table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String VIDEO_ID = "video_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
@@ -3048,14 +3160,14 @@
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 22329ba..a465b32 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1657,6 +1657,30 @@
     public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS =
             "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
 
+
+    /**
+     * Activity Action: Show screen that let user select enable (or disable) Content Capture.
+     * <p>
+     * Input: Nothing.
+     *
+     * <p>
+     * Output: {@link android.app.Activity#RESULT_OK} if user enabled Content Capture,
+     * {@link android.app.Activity#RESULT_CANCELED} if user disabled it, cancelled, or if the caller
+     * is not the Content Capture service associated with the user.
+     *
+     * <p>
+     * <b>NOTE: </b> Caller should call
+     * {@link android.view.contentcapture.ContentCaptureManager#isContentCaptureFeatureEnabled()}
+     * first to check whether the feature is already enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE =
+            "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
+
     // End of Intent actions for Settings
 
     /**
@@ -6103,7 +6127,7 @@
          * Indicates which clock face to show on lock screen and AOD while docked.
          * @hide
          */
-        private static final String DOCKED_CLOCK_FACE = "docked_clock_face";
+        public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
 
         /**
          * Set by the system to track if the user needs to see the call to action for
@@ -11707,6 +11731,14 @@
         public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
 
         /**
+         * Enable ART bytecode verification verifications for debuggable apps.
+         * 0 = disable, 1 = enable.
+         * @hide
+         */
+        public static final String ART_VERIFIER_VERIFY_DEBUGGABLE =
+                "art_verifier_verify_debuggable";
+
+        /**
          * Power manager specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -12318,6 +12350,14 @@
                 "angle_whitelist";
 
         /**
+         * Show the "ANGLE In Use" dialog box to the user when ANGLE is the OpenGL driver.
+         * The value is a boolean (1 or 0).
+         * @hide
+         */
+        public static final String GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX =
+                "show_angle_in_use_dialog_box";
+
+        /**
          * Game Driver global preference for all Apps.
          * 0 = Default
          * 1 = All Apps use Game Driver
@@ -12347,6 +12387,12 @@
         public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist";
 
         /**
+         * List of blacklists, each blacklist is a blacklist for a specific version of Game Driver.
+         * @hide
+         */
+        public static final String GAME_DRIVER_BLACKLISTS = "game_driver_blacklists";
+
+        /**
          * Apps on the whitelist that are allowed to use Game Driver.
          * The string is a list of application package names, seperated by comma.
          * i.e. <apk1>,<apk2>,...,<apkN>
@@ -12355,6 +12401,14 @@
         public static final String GAME_DRIVER_WHITELIST = "game_driver_whitelist";
 
         /**
+         * List of libraries in sphal accessible by Game Driver
+         * The string is a list of library names, separated by colon.
+         * i.e. <lib1>:<lib2>:...:<libN>
+         * @hide
+         */
+        public static final String GAME_DRIVER_SPHAL_LIBRARIES = "game_driver_sphal_libraries";
+
+        /**
          * Ordered GPU debug layer list for Vulkan
          * i.e. <layer1>:<layer2>:...:<layerN>
          * @hide
@@ -14354,6 +14408,7 @@
          * <pre>
          *     num_buckets          (int)
          *     collected_uids       (string)
+         *     minimum_total_cpu_usage_millis (int)
          * </pre>
          *
          * @hide
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 81d066d..ce83a57 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -61,8 +61,6 @@
  */
 @SystemApi
 @TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
@@ -128,6 +126,14 @@
     }
 
     /**
+     * Called when the Android system connects to service.
+     *
+     * <p>You should generally do initialization here rather than in {@link #onCreate}.
+     */
+    public void onConnected() {
+    }
+
+    /**
      * Asks the service to handle an "augmented" autofill request.
      *
      * <p>This method is called when the "stantard" autofill service cannot handle a request, which
@@ -160,6 +166,14 @@
             @NonNull FillCallback callback) {
     }
 
+    /**
+     * Called when the Android system disconnects from the service.
+     *
+     * <p> At this point this service may no longer be an active {@link AugmentedAutofillService}.
+     */
+    public void onDisconnected() {
+    }
+
     private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId,
             @NonNull ComponentName componentName, @NonNull AutofillId focusedId,
             @Nullable AutofillValue focusedValue, long requestTime,
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index f2a7a35..b989dd9 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -31,8 +31,6 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillCallback {
 
     private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index d7bc893..67f23d5 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -38,10 +38,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillController {
-    private static final String TAG = "FillController";
+    private static final String TAG = FillController.class.getSimpleName();
 
     private final AutofillProxy mProxy;
 
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index af9905f..9a97bb2 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -31,8 +31,6 @@
 @SystemApi
 // TODO(b/123100811): pass a requestId and/or sessionId?
 @TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillRequest {
 
     final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index f1e904a..2ac406c 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -30,8 +30,6 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillResponse {
 
     private final FillWindow mFillWindow;
@@ -53,8 +51,6 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
     public static final class Builder {
 
         private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 40e3a12..6e06754 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -63,10 +63,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillWindow implements AutoCloseable {
-    private static final String TAG = "FillWindow";
+    private static final String TAG = FillWindow.class.getSimpleName();
 
     private final Object mLock = new Object();
     private final CloseGuard mCloseGuard = CloseGuard.get();
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 1fb9032..334487d 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -15,32 +15,19 @@
  */
 package android.service.autofill.augmented;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
-import android.util.DebugUtils;
 import android.view.View;
 
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 
 /**
  * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
- * the intelligence service.
- *
- * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
- *
- * <ul>
- *   <li>A small area associated with suggestions (like a small strip in the top of the IME),
- *   returned by {@link #getSuggestionArea()}
- *   <li>The full area (like the full IME window), returned by {@link #getFullArea()}
- *   <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
- * </ul>
+ * the augmented autofill service.
  *
  * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
  * dimensions the smart suggestion window, so the service can use it to calculate the size of the
@@ -50,54 +37,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class PresentationParams {
 
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the top of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the left of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the right of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted by the IME.
-     */
-    public static final int FLAG_HOST_IME = 0x10;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
-     * window.
-     */
-    public static final int FLAG_HOST_SYSTEM = 0x20;
-
-    /** @hide */
-    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
-            FLAG_HINT_GRAVITY_TOP,
-            FLAG_HINT_GRAVITY_BOTTOM,
-            FLAG_HINT_GRAVITY_LEFT,
-            FLAG_HINT_GRAVITY_RIGHT,
-            FLAG_HOST_IME,
-            FLAG_HOST_SYSTEM
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface Flags {}
-
-
     // /** @hide */
     PresentationParams() {}
 
@@ -112,40 +53,7 @@
         return null;
     }
 
-    /**
-     * Gets the full area for the of the Smart Suggestion provider.
-     *
-     * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
-     * embeding the UI on its full area.
-     */
-    @Nullable
-    public Area getFullArea() {
-        return null;
-    }
-
-    /**
-     * Gets flags associated with the Smart Suggestion.
-     *
-     * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
-     * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
-     * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
-     * {@link #FLAG_HOST_SYSTEM},
-     */
-    public @Flags int getFlags() {
-        return 0;
-    }
-
-    /** @hide */
-    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        final int flags = getFlags();
-        if (flags > 0) {
-            pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
-        }
-    }
-
-    private static String flagsToString(int flags) {
-        return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
-    }
+    abstract void dump(String prefix, PrintWriter pw);
 
     /**
      * Area associated with a {@link PresentationParams Smart Suggestions} provider.
@@ -154,8 +62,6 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
     public abstract static class Area {
 
         /** @hide */
@@ -176,24 +82,6 @@
             return mBounds;
         }
 
-        /**
-         * Gets a subarea limited by given boundaries.
-         *
-         * @param bounds boundaries relative to this Area.
-         *
-         * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
-         * subaarea.
-         *
-         * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this
-         * full Area.
-         *
-         */
-        @Nullable
-        public Area getSubArea(@NonNull Rect bounds) {
-            // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test
-            return null;
-        }
-
         @Override
         public String toString() {
             return mBounds.toString();
@@ -220,13 +108,7 @@
         }
 
         @Override
-        public int getFlags() {
-            return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
-        }
-
-        @Override
         void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-            super.dump(prefix, pw);
             pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
         }
     }
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index ca6676d..bbcf7a9 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -28,7 +28,8 @@
 /**
  * Not needed anymore...
  *
- * @deprecated
+ * @deprecated ContentCaptureService should use
+ * {@code #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)} instead.
  *
  * @hide
  */
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c98f09e..d361a2c 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -34,6 +34,7 @@
 import android.os.RemoteException;
 import android.service.autofill.AutofillService;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
@@ -49,7 +50,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A service used to capture the content of the screen to provide contextual data in other areas of
@@ -164,6 +167,19 @@
     }
 
     /**
+     * @deprecated use {@link #setContentCaptureWhitelist(Set, Set)} instead
+     */
+    @Deprecated
+    public final void setContentCaptureWhitelist(@Nullable List<String> packages,
+            @Nullable List<ComponentName> activities) {
+        setContentCaptureWhitelist(toSet(packages), toSet(activities));
+    }
+
+    private <T> ArraySet<T> toSet(@Nullable List<T> set) {
+        return set == null ? null : new ArraySet<T>(set);
+    }
+
+    /**
      * Explicitly limits content capture to the given packages and activities.
      *
      * <p>To reset the whitelist, call it passing {@code null} to both arguments.
@@ -171,24 +187,29 @@
      * <p>Useful when the service wants to restrict content capture to a category of apps, like
      * chat apps. For example, if the service wants to support view captures on all activities of
      * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
-     * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
-     * Arrays.asList(new ComponentName("ChatApp2", "act1"),
+     * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
+     * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
      * new ComponentName("ChatApp2", "act2")));}
      */
-    public final void setContentCaptureWhitelist(@Nullable List<String> packages,
-            @Nullable List<ComponentName> activities) {
+    public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
         final IContentCaptureServiceCallback callback = mCallback;
         if (callback == null) {
             Log.w(TAG, "setContentCaptureWhitelist(): no server callback");
             return;
         }
+
         try {
-            callback.setContentCaptureWhitelist(packages, activities);
+            callback.setContentCaptureWhitelist(toList(packages), toList(activities));
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
     }
 
+    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
+        return set == null ? null : new ArrayList<T>(set);
+    }
+
     /**
      * Called when the Android system connects to service.
      *
@@ -263,6 +284,24 @@
     }
 
     /**
+     * Disables the Content Capture service for the given user.
+     */
+    public final void disableContentCaptureServices() {
+        if (DEBUG) Log.d(TAG, "disableContentCaptureServices()");
+
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "disableContentCaptureServices(): no server callback");
+            return;
+        }
+        try {
+            callback.disableSelf();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called when the Android system disconnects from the service.
      *
      * <p> At this point this service may no longer be an active {@link AutofillService}.
@@ -339,7 +378,7 @@
             }
             switch (event.getType()) {
                 case ContentCaptureEvent.TYPE_SESSION_STARTED:
-                    final ContentCaptureContext clientContext = event.getClientContext();
+                    final ContentCaptureContext clientContext = event.getContentCaptureContext();
                     clientContext.setParentSessionId(event.getParentSessionId());
                     mSessionUids.put(sessionIdString, uid);
                     onCreateContentCaptureSession(clientContext, sessionId);
@@ -383,8 +422,8 @@
         }
         final Integer rightUid = mSessionUids.get(sessionId);
         if (rightUid == null) {
-            if (DEBUG) {
-                Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+            if (VERBOSE) {
+                Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
                         + ": " + mSessionUids);
             }
             // Just ignore, as the session could have been finished already
diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
index 2a729b6..8bc8def 100644
--- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -17,7 +17,6 @@
 package android.service.contentcapture;
 
 import android.content.ComponentName;
-import com.android.internal.os.IResultReceiver;
 
 import java.util.List;
 
@@ -28,4 +27,5 @@
  */
 oneway interface IContentCaptureServiceCallback {
     void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
-}
+    void disableSelf();
+ }
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index ffb524d..a46d047 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -76,6 +76,11 @@
  * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
  * priority.
  *
+ * <p>Old implementations of EuiccService may support passing in slot IDs equal to
+ * {@link android.telephony.SubscriptionManager#INVALID_SIM_SLOT_INDEX}, which allows the LPA to
+ * decide which eUICC to target when there are multiple eUICCs. This behavior is not supported in
+ * Android Q or later.
+ *
  * @hide
  */
 @SystemApi
@@ -546,7 +551,7 @@
                         int resultCode = EuiccService.this.onDownloadSubscription(
                                 slotId, subscription, switchAfterDownload, forceDeactivateSim);
                         result = new DownloadSubscriptionResult(resultCode,
-                            0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID);
+                            0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
                     }
                     try {
                         callback.onComplete(result);
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 93189b3..bddc5ef 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,13 +15,22 @@
  */
 package android.service.notification;
 
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 /**
  * Ranking updates from the Assistant.
+ *
+ * The updates are provides as a {@link Bundle} of signals, using the keys provided in this
+ * class.
+ * Each {@code KEY} specifies what type of data it supports and what kind of Adjustment it
+ * realizes on the notification rankings.
+ *
+ * Notifications affected by the Adjustment will be re-ranked if necessary.
  */
 public final class Adjustment implements Parcelable {
     private final String mPackage;
@@ -36,13 +45,13 @@
      * See {@link android.app.Notification.Builder#addPerson(String)}.
      * @hide
      */
+    @SystemApi
     public static final String KEY_PEOPLE = "key_people";
     /**
      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
      * users. If a user chooses to snooze a notification until one of these criterion, the
      * assistant will be notified via
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
-     * @hide
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     /**
@@ -102,7 +111,9 @@
      * @param signals A bundle of signals that should inform notification display, ordering, and
      *                interruptiveness.
      * @param explanation A human-readable justification for the adjustment.
+     * @hide
      */
+    @SystemApi
     public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) {
         mPackage = pkg;
         mKey = key;
@@ -111,7 +122,30 @@
         mUser = user;
     }
 
-    private Adjustment(Parcel in) {
+    /**
+     * Create a notification adjustment.
+     *
+     * @param pkg The package of the notification.
+     * @param key The notification key.
+     * @param signals A bundle of signals that should inform notification display, ordering, and
+     *                interruptiveness.
+     * @param explanation A human-readable justification for the adjustment.
+     * @param userHandle User handle for for whose the adjustments will be applied.
+     */
+    public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation,
+            UserHandle userHandle) {
+        mPackage = pkg;
+        mKey = key;
+        mSignals = signals;
+        mExplanation = explanation;
+        mUser = userHandle.getIdentifier();
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected Adjustment(Parcel in) {
         if (in.readInt() == 1) {
             mPackage = in.readString();
         } else {
@@ -159,10 +193,16 @@
         return mSignals;
     }
 
+    /** @hide */
+    @SystemApi
     public int getUser() {
         return mUser;
     }
 
+    public UserHandle getUserHandle() {
+        return UserHandle.of(mUser);
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e93b158..a697248 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
@@ -103,7 +104,6 @@
      *
      * @param sbn the notification to snooze
      * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
-     * @hide
      */
     abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
             String snoozeCriterionId);
@@ -111,12 +111,13 @@
     /**
      * A notification was posted by an app. Called before post.
      *
+     * <p>Note: this method is only called if you don't override
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+     *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 100ms.
      */
-    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
-        return null;
-    }
+    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
 
     /**
      * A notification was posted by an app. Called before post.
@@ -240,12 +241,11 @@
     /**
      * Inform the notification manager about un-snoozing a specific notification.
      * <p>
-     * This should only be used for notifications snoozed by this listener using
-     * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
+     * This should only be used for notifications snoozed because of a contextual snooze suggestion
+     * you provided via {@link Adjustment#KEY_SNOOZE_CRITERIA}. Once un-snoozed, you will get a
      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
      * notification.
      * @param key The key of the notification to snooze
-     * @hide
      */
     public final void unsnoozeNotification(String key) {
         if (!isBound()) return;
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 814b477..ebfabbf 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -16,6 +16,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -98,7 +99,11 @@
     public NotificationStats() {
     }
 
-    private NotificationStats(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected NotificationStats(Parcel in) {
         mSeen = in.readByte() != 0;
         mExpanded = in.readByte() != 0;
         mDirectReplied = in.readByte() != 0;
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 551bb8a..954dc39 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -372,6 +372,13 @@
     /**
      * @hide
      */
+    public void clearPackageContext() {
+        mContext = null;
+    }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public Context getPackageContext(Context context) {
         if (mContext == null) {
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index ea2a25d..e3e63e5 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -342,37 +342,17 @@
     }
 
     /**
-     * Requests that the voice state UI indicate the given state.
+     * Provide hints to be reflected in the system UI.
      *
-     * @param state value indicating whether the assistant is listening, fulfilling, etc.
+     * @param hints Arguments used to show UI.
      */
-    public final void setVoiceState(int state) {
-        try {
-            mSystemService.setVoiceState(mInterface, state);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+    public final void setUiHints(@NonNull Bundle hints) {
+        if (hints == null) {
+            throw new IllegalArgumentException("Hints must be non-null");
         }
-    }
 
-    /**
-     * Displays the given voice transcription contents.
-     */
-    public final void setTranscription(@NonNull String transcription) {
         try {
-            mSystemService.setTranscription(mInterface, transcription);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Hides transcription.
-     *
-     * @param immediate if {@code true}, remove before transcription animation completes.
-     */
-    public final void clearTranscription(boolean immediate) {
-        try {
-            mSystemService.clearTranscription(mInterface, immediate);
+            mSystemService.setUiHints(mInterface, hints);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index b197c8a..c042a8c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1068,9 +1068,13 @@
          * For multiple display environment, multiple engines can be created to render on each
          * display, but these displays may have different densities. Use this context to get the
          * corresponding resources for currently display, avoiding the context of the service.
+         * <p>
+         * The display context will never be {@code null} after
+         * {@link Engine#onCreate(SurfaceHolder)} has been called.
          *
          * @return A {@link Context} for current display.
          */
+        @Nullable
         public Context getDisplayContext() {
             return mDisplayContext;
         }
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index be772af..5754014 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -77,6 +77,13 @@
      */
     public static final int ALIGN_BASELINE = 1;
 
+    /**
+     * A constant indicating that this span should be vertically centered between
+     * the top and the lowest descender.
+     * @hide
+     */
+    public static final int ALIGN_CENTER = 2;
+
     protected final int mVerticalAlignment;
 
     @UnsupportedAppUsage
@@ -142,6 +149,8 @@
         int transY = bottom - b.getBounds().bottom;
         if (mVerticalAlignment == ALIGN_BASELINE) {
             transY -= paint.getFontMetricsInt().descent;
+        } else if (mVerticalAlignment == ALIGN_CENTER) {
+            transY = (bottom - top) / 2 - b.getBounds().height() / 2;
         }
 
         canvas.translate(x, transY);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1ca6398..67a4015 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -54,9 +54,7 @@
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
         DEFAULT_FLAGS.put("settings_slice_injection", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
-        DEFAULT_FLAGS.put("settings_wifi_dpp", "true");
         DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true");
-        DEFAULT_FLAGS.put("settings_wifi_sharing", "true");
         DEFAULT_FLAGS.put("settings_mainline_module", "false");
         DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 56558d0..0eeef70 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -166,6 +166,26 @@
     }
 
     /**
+     * Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link
+     * #lerp}{@code (a, b, s)}
+     *
+     * <p>If {@code a == b}, then this function will return 0.
+     */
+    public static float lerpInv(float a, float b, float value) {
+        return a != b ? ((value - a) / (b - a)) : 0.0f;
+    }
+
+    /** Returns the single argument constrained between [0.0, 1.0]. */
+    public static float saturate(float value) {
+        return constrain(value, 0.0f, 1.0f);
+    }
+
+    /** Returns the saturated (constrained between [0, 1]) result of {@link #lerpInv}. */
+    public static float lerpInvSat(float a, float b, float value) {
+        return saturate(lerpInv(a, b, value));
+    }
+
+    /**
      * Returns an interpolated angle in degrees between a set of start and end
      * angles.
      * <p>
@@ -195,6 +215,32 @@
     }
 
     /**
+     * Calculates a value in [rangeMin, rangeMax] that maps value in [valueMin, valueMax] to
+     * returnVal in [rangeMin, rangeMax].
+     * <p>
+     * Always returns a constrained value in the range [rangeMin, rangeMax], even if value is
+     * outside [valueMin, valueMax].
+     * <p>
+     * Eg:
+     *    constrainedMap(0f, 100f, 0f, 1f, 0.5f) = 50f
+     *    constrainedMap(20f, 200f, 10f, 20f, 20f) = 200f
+     *    constrainedMap(20f, 200f, 10f, 20f, 50f) = 200f
+     *    constrainedMap(10f, 50f, 10f, 20f, 5f) = 10f
+     *
+     * @param rangeMin minimum of the range that should be returned.
+     * @param rangeMax maximum of the range that should be returned.
+     * @param valueMin minimum of range to map {@code value} to.
+     * @param valueMax maximum of range to map {@code value} to.
+     * @param value to map to the range [{@code valueMin}, {@code valueMax}]. Note, can be outside
+     *              this range, resulting in a clamped value.
+     * @return the mapped value, constrained to [{@code rangeMin}, {@code rangeMax}.
+     */
+    public static float constrainedMap(
+            float rangeMin, float rangeMax, float valueMin, float valueMax, float value) {
+        return lerp(rangeMin, rangeMax, lerpInvSat(valueMin, valueMax, value));
+    }
+
+    /**
      * Perform Hermite interpolation between two values.
      * Eg:
      *   smoothStep(0, 0.5f, 0.5f) = 1f
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index ace4bf4..29ced3e 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -16,7 +16,14 @@
 
 package android.util;
 
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.IActivityManager;
+import android.content.Context;
 import android.os.IStatsManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -31,7 +38,10 @@
 
     private static IStatsManager sService;
 
-    private StatsLog() {}
+    private static Object sLogLock = new Object();
+
+    private StatsLog() {
+    }
 
     /**
      * Logs a start event.
@@ -40,11 +50,13 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStart(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging start");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(label,
@@ -52,7 +64,9 @@
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging start");
+                }
                 return false;
             }
         }
@@ -65,18 +79,22 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStop(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging stop");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                }
                 return false;
             }
         }
@@ -89,11 +107,13 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logEvent(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging event");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(
@@ -101,7 +121,51 @@
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging event");
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Logs an event for binary push for module updates.
+     *
+     * @param trainName        name of install train.
+     * @param trainVersionCode version code of the train.
+     * @param options          optional flags about this install.
+     * @param state            current install state.
+     * @param experimentIds    experiment ids.
+     * @return True if the log request was sent to statsd.
+     */
+    @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
+    public static boolean logBinaryPushStateChanged(@NonNull String trainName,
+            long trainVersionCode, int options, int state,
+            @NonNull long[] experimentIds) {
+        synchronized (sLogLock) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging event");
+                    }
+                    return false;
+                }
+                int userId = IActivityManager.Stub.asInterface(
+                        ServiceManager.getService("activity"))
+                        .getCurrentUser()
+                        .id;
+                service.sendBinaryPushStateChangedAtom(
+                        trainName, trainVersionCode, options, state, experimentIds);
+                return true;
+            } catch (RemoteException e) {
+                sService = null;
+                if (DEBUG) {
+                    Slog.d(TAG,
+                            "Failed to connect to StatsCompanionService when logging "
+                                    + "BinaryPushStateChanged");
+                }
                 return false;
             }
         }
@@ -118,7 +182,7 @@
     /**
      * Add a log to the stats log.
      *
-     * @param id The id of the atom
+     * @param id     The id of the atom
      * @param params The parameters of the atom's message.
      */
     public static void write(int id, @NonNull Object... params) {
@@ -128,4 +192,13 @@
                         (boolean) params[4], (int) params[5]);
         }
     }
+
+    private static void enforceDumpCallingPermission(Context context) {
+        context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission.");
+    }
+
+    private static void enforcesageStatsCallingPermission(Context context) {
+        context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
+                "Need PACKAGE_USAGE_STATS permission.");
+    }
 }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 62ed901..f37c916 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -336,7 +336,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 mPrefetcher.prefetchAccessibilityNodeInfos(
                         root, virtualDescendantId, flags, infos, arguments);
             }
@@ -448,7 +448,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
                 if (provider != null) {
                     infos = provider.findAccessibilityNodeInfosByText(text,
@@ -531,7 +531,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 switch (focusType) {
                     case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
                         View host = mViewRootImpl.mAccessibilityFocusedHost;
@@ -621,7 +621,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 View nextView = root.focusSearch(direction);
                 if (nextView != null) {
                     next = nextView.createAccessibilityNodeInfo();
@@ -676,7 +676,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View target = findViewByAccessibilityId(accessibilityViewId);
-            if (target != null) {
+            if (target != null && isShown(target)) {
                 if (action == R.id.accessibilityActionClickOnClickableSpan) {
                     // Handle this hidden action separately
                     succeeded = handleClickableSpanActionUiThread(
@@ -759,9 +759,7 @@
         if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
             return mViewRootImpl.mView;
         } else {
-            final View foundView =
-                    AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
-            return isShown(foundView) ? foundView : null;
+            return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
         }
     }
 
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index c77500a..696e048 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.annotation.StyleRes;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 
 /**
  * A context wrapper that allows you to modify or replace the theme of the
@@ -31,7 +33,7 @@
 public class ContextThemeWrapper extends ContextWrapper {
     @UnsupportedAppUsage
     private int mThemeResource;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768723)
     private Resources.Theme mTheme;
     @UnsupportedAppUsage
     private LayoutInflater mInflater;
@@ -146,6 +148,15 @@
         }
     }
 
+    /**
+     * Set the configure the current theme. If null is provided then the default Theme is returned
+     * on the next call to {@link #getTheme()}
+     * @param theme Theme to consume in the wrapper, a value of null resets the theme to the default
+     */
+    public void setTheme(@Nullable Resources.Theme theme) {
+        mTheme = theme;
+    }
+
     /** @hide */
     @Override
     @UnsupportedAppUsage
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 49bae28..cb5100a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -76,7 +76,7 @@
     private final int mLayerStack;
     private final int mFlags;
     private final int mType;
-    private final String mAddress;
+    private final DisplayAddress mAddress;
     private final int mOwnerUid;
     private final String mOwnerPackageName;
     private final Resources mResources;
@@ -557,7 +557,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public String getAddress() {
+    public DisplayAddress getAddress() {
         return mAddress;
     }
 
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
new file mode 100644
index 0000000..17ea4c4
--- /dev/null
+++ b/core/java/android/view/DisplayAddress.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** Display identifier that is stable across reboots.
+ *
+ * @hide
+ */
+public abstract class DisplayAddress implements Parcelable {
+    /**
+     * Creates an address for a physical display given its stable ID.
+     *
+     * A physical display ID is stable if the display can be identified using EDID information.
+     *
+     * @param physicalDisplayId A physical display ID.
+     * @return The {@link Physical} address, or {@code null} if the ID is not stable.
+     * @see SurfaceControl#getPhysicalDisplayIds
+     */
+    @Nullable
+    public static Physical fromPhysicalDisplayId(long physicalDisplayId) {
+        final Physical address = new Physical(physicalDisplayId);
+        return address.getModel() == 0 ? null : address;
+    }
+
+    /**
+     * Creates an address for a network display given its MAC address.
+     *
+     * @param macAddress A MAC address in colon notation.
+     * @return The {@link Network} address.
+     */
+    @NonNull
+    public static Network fromMacAddress(String macAddress) {
+        return new Network(macAddress);
+    }
+
+    /**
+     * Address for a physically connected display.
+     *
+     * A {@link Physical} address is represented by a 64-bit identifier combining the port and model
+     * of a display. The port, located in the least significant byte, uniquely identifies a physical
+     * connector on the device for display output like eDP or HDMI. The model, located in the upper
+     * bits, uniquely identifies a display model across manufacturers by encoding EDID information.
+     */
+    public static final class Physical extends DisplayAddress {
+        private static final int PHYSICAL_DISPLAY_ID_MODEL_SHIFT = 8;
+        private static final int PORT_MASK = 0xFF;
+
+        private final long mPhysicalDisplayId;
+
+        /**
+         * Physical port to which the display is connected.
+         */
+        public byte getPort() {
+            return (byte) mPhysicalDisplayId;
+        }
+
+        /**
+         * Model identifier unique across manufacturers.
+         */
+        public long getModel() {
+            return mPhysicalDisplayId >>> PHYSICAL_DISPLAY_ID_MODEL_SHIFT;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return other instanceof Physical
+                    && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("{")
+                    .append("port=").append(getPort() & PORT_MASK)
+                    .append(", model=0x").append(Long.toHexString(getModel()))
+                    .append("}")
+                    .toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Long.hashCode(mPhysicalDisplayId);
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeLong(mPhysicalDisplayId);
+        }
+
+        private Physical(long physicalDisplayId) {
+            mPhysicalDisplayId = physicalDisplayId;
+        }
+
+        public static final Parcelable.Creator<Physical> CREATOR =
+                new Parcelable.Creator<Physical>() {
+                    @Override
+                    public Physical createFromParcel(Parcel in) {
+                        return new Physical(in.readLong());
+                    }
+
+                    @Override
+                    public Physical[] newArray(int size) {
+                        return new Physical[size];
+                    }
+                };
+    }
+
+    /**
+     * Address for a network-connected display.
+     */
+    public static final class Network extends DisplayAddress {
+        private final String mMacAddress;
+
+        @Override
+        public boolean equals(Object other) {
+            return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress);
+        }
+
+        @Override
+        public String toString() {
+            return mMacAddress;
+        }
+
+        @Override
+        public int hashCode() {
+            return mMacAddress.hashCode();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(mMacAddress);
+        }
+
+        private Network(String macAddress) {
+            mMacAddress = macAddress;
+        }
+
+        public static final Parcelable.Creator<Network> CREATOR =
+                new Parcelable.Creator<Network>() {
+                    @Override
+                    public Network createFromParcel(Parcel in) {
+                        return new Network(in.readString());
+                    }
+
+                    @Override
+                    public Network[] newArray(int size) {
+                        return new Network[size];
+                    }
+                };
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ad8fee9..3aa779b 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -61,7 +61,7 @@
      * Display address, or null if none.
      * Interpretation varies by display type.
      */
-    public String address;
+    public DisplayAddress address;
 
     /**
      * The human-readable name of the display.
@@ -385,7 +385,7 @@
         layerStack = source.readInt();
         flags = source.readInt();
         type = source.readInt();
-        address = source.readString();
+        address = source.readParcelable(null);
         name = source.readString();
         appWidth = source.readInt();
         appHeight = source.readInt();
@@ -432,7 +432,7 @@
         dest.writeInt(layerStack);
         dest.writeInt(this.flags);
         dest.writeInt(type);
-        dest.writeString(address);
+        dest.writeParcelable(address, flags);
         dest.writeString(name);
         dest.writeInt(appWidth);
         dest.writeInt(appHeight);
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index 74c801b..3286bd6 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -20,6 +20,7 @@
 import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
+import android.os.Build;
 import android.widget.FrameLayout;
 
 import java.util.ArrayList;
@@ -135,12 +136,12 @@
         return ghostView;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static GhostView addGhost(View view, ViewGroup viewGroup) {
         return addGhost(view, viewGroup, null);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static void removeGhost(View view) {
         GhostView ghostView = view.mGhostView;
         if (ghostView != null) {
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index b28ac47..f13cb5a 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -129,7 +129,7 @@
     static final Class<?>[] mConstructorSignature = new Class[] {
             Context.class, AttributeSet.class};
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769490)
     private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
             new HashMap<String, Constructor<? extends View>>();
 
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1dbc46b..6cfc9f2 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -25,6 +25,7 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
 import android.os.Build;
+import android.os.Handler;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.VirtualRefBasePtr;
@@ -84,6 +85,7 @@
 
     private VirtualRefBasePtr mNativePtr;
 
+    private Handler mHandler;
     private RenderNode mTarget;
     private View mViewTarget;
     private int mRenderProperty = -1;
@@ -222,6 +224,9 @@
     private void moveToRunningState() {
         mState = STATE_RUNNING;
         if (mNativePtr != null) {
+            if (mHandler == null) {
+                mHandler = new Handler();
+            }
             nStart(mNativePtr.get());
         }
         notifyStartListeners();
@@ -497,7 +502,7 @@
     // Called by native
     @UnsupportedAppUsage
     private static void callOnFinished(RenderNodeAnimator animator) {
-        animator.onFinished();
+        animator.mHandler.post(animator::onFinished);
     }
 
     @Override
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 47b206ca..2097812 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -453,7 +453,7 @@
      */
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        destroyHardwareResources();
+        clearContent();
     }
 
     private static void destroyResources(View view) {
@@ -735,7 +735,9 @@
             if (callback != null) {
                 setFrameCallback(callback);
             }
-            syncAndDrawFrame(vsync);
+            createRenderRequest()
+                    .setVsyncTime(vsync)
+                    .syncAndDraw();
         }
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 83df33e..a78f2b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24,6 +24,7 @@
 
 import android.animation.AnimatorInflater;
 import android.animation.StateListAnimator;
+import android.annotation.AttrRes;
 import android.annotation.CallSuper;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -5108,7 +5109,7 @@
     private SparseIntArray mAttributeSourceResId;
 
     @Nullable
-    private int[] mAttributeResolutionStack;
+    private SparseArray<int[]> mAttributeResolutionStacks;
 
     @StyleRes
     private int mExplicitStyle;
@@ -5963,11 +5964,12 @@
      * <b>Note:</b> this method will only return actual values if the view attribute debugging
      * is enabled in Android developer options.
      *
+     * @param attribute Attribute resource ID for which the resolution stack should be returned.
      * @return ordered list of resource ID that are considered when resolving attribute values for
      * this {@link View}.
      */
     @NonNull
-    public List<Integer> getAttributeResolutionStack() {
+    public List<Integer> getAttributeResolutionStack(@AttrRes int attribute) {
         ArrayList<Integer> stack = new ArrayList<>();
         if (!sDebugViewAttributes) {
             return stack;
@@ -5975,8 +5977,12 @@
         if (mSourceLayoutId != ID_NULL) {
             stack.add(mSourceLayoutId);
         }
-        for (int i = 0; i < mAttributeResolutionStack.length; i++) {
-            stack.add(mAttributeResolutionStack[i]);
+        int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute);
+        if (attributeResolutionStack == null) {
+            return stack;
+        }
+        for (int i = 0; i < attributeResolutionStack.length; i++) {
+            stack.add(attributeResolutionStack[i]);
         }
         return stack;
     }
@@ -6143,9 +6149,13 @@
             return;
         }
 
-        mAttributeResolutionStack = context.getTheme().getAttributeResolutionStack(
+        int[] attributeResolutionStack = context.getTheme().getAttributeResolutionStack(
                 defStyleAttr, defStyleRes, mExplicitStyle);
 
+        if (mAttributeResolutionStacks == null) {
+            mAttributeResolutionStacks = new SparseArray<>();
+        }
+
         if (mAttributeSourceResId == null) {
             mAttributeSourceResId = new SparseIntArray();
         }
@@ -6154,6 +6164,7 @@
         for (int j = 0; j < indexCount; ++j) {
             final int index = t.getIndex(j);
             mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index, 0));
+            mAttributeResolutionStacks.append(styleable[index], attributeResolutionStack);
         }
     }
 
@@ -6456,6 +6467,16 @@
         arr.recycle();
     }
 
+    private void initializeScrollBarDrawable() {
+        initScrollCache();
+
+        if (mScrollCache.scrollBar == null) {
+            mScrollCache.scrollBar = new ScrollBarDrawable();
+            mScrollCache.scrollBar.setState(getDrawableState());
+            mScrollCache.scrollBar.setCallback(this);
+        }
+    }
+
     /**
      * <p>
      * Initializes the scrollbars from a given set of styled attributes. This
@@ -6541,6 +6562,106 @@
         resolvePadding();
     }
 
+    /**
+     * Defines the vertical scrollbar thumb drawable
+     * @attr ref android.R.styleable#View_scrollbarThumbVertical
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public void setVerticalScrollbarThumbDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setVerticalThumbDrawable(drawable);
+    }
+
+    /**
+     * Defines the vertical scrollbar track drawable
+     * @attr ref android.R.styleable#View_scrollbarTrackVertical
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public void setVerticalScrollbarTrackDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setVerticalTrackDrawable(drawable);
+    }
+
+    /**
+     * Defines the horizontal thumb drawable
+     * @attr ref android.R.styleable#View_scrollbarThumbHorizontal
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public void setHorizontalScrollbarThumbDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setHorizontalThumbDrawable(drawable);
+    }
+
+    /**
+     * Defines the horizontal track drawable
+     * @attr ref android.R.styleable#View_scrollbarTrackHorizontal
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public void setHorizontalScrollbarTrackDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setHorizontalTrackDrawable(drawable);
+    }
+
+    /**
+     * Returns the currently configured Drawable for the thumb of the vertical scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getVerticalScrollbarThumbDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getVerticalThumbDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the track of the vertical scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getVerticalScrollbarTrackDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getVerticalTrackDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the thumb of the horizontal scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getHorizontalScrollbarThumbDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalThumbDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the track of the horizontal scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getHorizontalScrollbarTrackDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalTrackDrawable() : null;
+    }
+
     private void initializeScrollIndicatorsInternal() {
         // Some day maybe we'll break this into top/left/start/etc. and let the
         // client control it. Until then, you can have any scroll indicator you
@@ -9342,17 +9463,24 @@
      * the Content Capture events associated with this view or its view hierarchy (if it's a
      * {@link ViewGroup}).
      *
+     * <p>For example, if your activity is associated with a web domain, first you would need to
+     * set the context for the main DOM:
+     *
+     * <pre>
+     *   ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+     *   mainSession.setContentCaptureContext(ContentCaptureContext.forLocusId(Uri.parse(myUrl));
+     * <pre>
+     *
+     * <p>Then if the page had an {@code IFRAME}, you would create a new session for it:
+     *
      * <p>For example, if your activity is associated with a web domain, you could create a session
      * {@code onCreate()} and associate it with the root view of the activity:
      *
      * <pre>
-     *   ContentCaptureSession oldSession = rootView.getContentCaptureSession();
-     *   if (oldSession != null) {
-     *     ContentCaptureSession newSession = oldSession.createContentCaptureSession(new
-     *        ContentCaptureContext.Builder().setUri(myUrl).build());
-     *     rootView.setContentCaptureSession(newSession);
-     *  }
-     * </pre>
+     *   ContentCaptureSession iframeSession = mainSession.createContentCaptureSession(
+     *       ContentCaptureContext.forLocusId(Uri.parse(iframeUrl)));
+     *   iframeView.setContentCaptureSession(iframeSession);
+     * <pre>
      *
      * @param contentCaptureSession a session created by
      * {@link ContentCaptureSession#createContentCaptureSession(
@@ -9366,7 +9494,7 @@
      * Gets the session used to notify Content Capture events.
      *
      * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
-     * inherited by ancestore, default session or {@code null} if content capture is disabled for
+     * inherited by ancestors, default session or {@code null} if content capture is disabled for
      * this view.
      */
     @Nullable
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index aaf1d11..f2474a5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4301,6 +4301,20 @@
         return i;
     }
 
+    /**
+     * The public version of getChildDrawingOrder().
+     *
+     * Returns the index of the child to draw for this iteration.
+     *
+     * @param i The current iteration.
+     * @return The index of the child to draw this iteration.
+     *
+     * @see #getChildDrawingOrder(int, int)}
+     */
+    public final int getChildDrawingOrder(int i) {
+        return getChildDrawingOrder(getChildCount(), i);
+    }
+
     private boolean hasChildWithZ() {
         for (int i = 0; i < mChildrenCount; i++) {
             if (mChildren[i].getZ() != 0) return true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1a782ee..b1fee2d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3404,21 +3404,25 @@
                     .captureFrameCommitCallbacks();
             if (mReportNextDraw) {
                 usingAsyncReport = true;
-                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                    // TODO: Use the frame number
-                    pendingDrawFinished();
-                    if (commitCallbacks != null) {
-                        for (int i = 0; i < commitCallbacks.size(); i++) {
-                            commitCallbacks.get(i).run();
-                        }
-                    }
-                });
+                final Handler handler = mAttachInfo.mHandler;
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+                        handler.post(() -> {
+                            // TODO: Use the frame number
+                            pendingDrawFinished();
+                            if (commitCallbacks != null) {
+                                for (int i = 0; i < commitCallbacks.size(); i++) {
+                                    commitCallbacks.get(i).run();
+                                }
+                            }
+                        }));
             } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
-                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                    for (int i = 0; i < commitCallbacks.size(); i++) {
-                        commitCallbacks.get(i).run();
-                    }
-                });
+                final Handler handler = mAttachInfo.mHandler;
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+                        handler.post(() -> {
+                            for (int i = 0; i < commitCallbacks.size(); i++) {
+                                commitCallbacks.get(i).run();
+                            }
+                        }));
             }
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 097f368..3544a87 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -622,11 +622,10 @@
     /** @hide */
     public interface WindowControllerCallback {
         /**
-         * Moves the activity from
-         * Moves the activity from {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing
-         * mode to {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+         * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing
+         * mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
          */
-        void exitFreeformMode() throws RemoteException;
+        void toggleFreeformWindowingMode() throws RemoteException;
 
         /**
          * Puts the activity in picture-in-picture mode if the activity supports.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e3833c0..453c5e3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
@@ -269,6 +270,16 @@
         return views;
     }
 
+    /**
+     * @return the list of all views attached to the global window manager
+     */
+    @NonNull
+    public ArrayList<View> getWindowViews() {
+        synchronized (mLock) {
+            return new ArrayList<>(mViews);
+        }
+    }
+
     public View getWindowView(IBinder windowToken) {
         synchronized (mLock) {
             final int numViews = mViews.size();
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 06207a9..384cdbb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -174,7 +174,7 @@
 
     final Handler.Callback mCallback;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     boolean mIsEnabled;
 
     int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5e5c826..a6b40ed 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -78,6 +78,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
@@ -1780,12 +1781,20 @@
     }
 
     /**
+     * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Deprecated
+    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
+            @Nullable List<ComponentName> activities) {
+        // TODO(b/123100824): implement
+    }
+
+    /**
      * Explicitly limits augmented autofill to the given packages and activities.
      *
-     * <p>When the whitelist is set, it overrides the values passed to
-     * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}
-     * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
-     *
      * <p>To reset the whitelist, call it passing {@code null} to both arguments.
      *
      * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
@@ -1803,10 +1812,8 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
-    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
-            @Nullable List<ComponentName> activities) {
+    public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
         // TODO(b/123100824): implement
     }
 
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index acb81e0..13e8a65 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -20,10 +20,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-
 /**
  * A session that is explicitly created by the app (and hence is a descendant of
  * {@link MainContentCaptureSession}).
@@ -35,21 +31,11 @@
     @NonNull
     private final ContentCaptureSession mParent;
 
-    /**
-     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
-     * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
-     * context.
-     *
-     * @hide
-     */
-    @NonNull
-    private final ContentCaptureContext mClientContext;
-
     /** @hide */
     protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
             @NonNull ContentCaptureContext clientContext) {
+        super(clientContext);
         mParent = parent;
-        mClientContext = Preconditions.checkNotNull(clientContext);
     }
 
     @Override
@@ -73,6 +59,11 @@
     }
 
     @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        getMainCaptureSession().notifyContextUpdated(mId, context);
+    }
+
+    @Override
     void onDestroy() {
         getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
     }
@@ -101,13 +92,4 @@
     boolean isContentCaptureEnabled() {
         return getMainCaptureSession().isContentCaptureEnabled();
     }
-
-    @Override
-    void dump(String prefix, PrintWriter pw) {
-        if (mClientContext != null) {
-            // NOTE: we don't dump clientContent because it could have PII
-            pw.print(prefix); pw.println("hasClientContext");
-        }
-        super.dump(prefix, pw);
-    }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 6a9759d..8bb4d21 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -23,7 +23,7 @@
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
+import android.content.LocusId;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -88,8 +88,7 @@
 
     // Fields below are set by app on Builder
     private final @Nullable Bundle mExtras;
-    private final @Nullable Uri mUri;
-    private final @Nullable String mAction;
+    private final @Nullable LocusId mId;
 
     // Fields below are set by server when the session starts
     private final @Nullable ComponentName mComponentName;
@@ -106,13 +105,11 @@
         if (clientContext != null) {
             mHasClientContext = true;
             mExtras = clientContext.mExtras;
-            mUri = clientContext.mUri;
-            mAction = clientContext.mAction;
+            mId = clientContext.mId;
         } else {
             mHasClientContext = false;
             mExtras = null;
-            mUri = null;
-            mAction = null;
+            mId = null;
         }
         mComponentName = Preconditions.checkNotNull(componentName);
         mTaskId = taskId;
@@ -123,8 +120,7 @@
     private ContentCaptureContext(@NonNull Builder builder) {
         mHasClientContext = true;
         mExtras = builder.mExtras;
-        mUri = builder.mUri;
-        mAction = builder.mAction;
+        mId = builder.mId;
 
         mComponentName  = null;
         mTaskId = mFlags = 0;
@@ -135,38 +131,18 @@
      * Gets the (optional) extras set by the app (through {@link Builder#setExtras(Bundle)}).
      *
      * <p>It can be used to provide vendor-specific data that can be modified and examined.
-     *
-     * @hide
      */
-    @SystemApi
-    @TestApi
     @Nullable
     public Bundle getExtras() {
         return mExtras;
     }
 
     /**
-     * Gets the (optional) URI set by the app (through {@link Builder#setUri(Uri)}).
-     *
-     * @hide
+     * Gets the context id.
      */
-    @SystemApi
-    @TestApi
-    @Nullable
-    public Uri getUri() {
-        return mUri;
-    }
-
-    /**
-     * Gets the (optional) action set by the app (through {@link Builder#setAction(String)}).
-     *
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    @Nullable
-    public String getAction() {
-        return mAction;
+    @NonNull
+    public LocusId getLocusId() {
+        return mId;
     }
 
     /**
@@ -236,13 +212,38 @@
     }
 
     /**
+     * Helper that creates a {@link ContentCaptureContext} associated with the given {@code uri}.
+     */
+    public static ContentCaptureContext forLocusId(@NonNull Uri uri) {
+        return new Builder(new LocusId(uri)).build();
+    }
+
+    /**
      * Builder for {@link ContentCaptureContext} objects.
      */
     public static final class Builder {
         private Bundle mExtras;
-        private Uri mUri;
+        private final LocusId mId;
         private boolean mDestroyed;
-        private String mAction;
+
+        /**
+         * Creates a new builder.
+         *
+         * <p>The context must have an id, which is usually one of the following:
+         *
+         * <ul>
+         *   <li>A URL representing a web page (or {@code IFRAME}) that's being rendered by the
+         *   activity (See {@link View#setContentCaptureSession(ContentCaptureSession)} for an
+         *   example).
+         *   <li>A unique identifier of the application state (for example, a conversation between
+         *   2 users in a chat app).
+         *
+         * @param id id associated with this context.
+         */
+        // TODO(b/123577059): make sure this is well documented and understandable
+        public Builder(@NonNull LocusId id) {
+            mId = Preconditions.checkNotNull(id);
+        }
 
         /**
          * Sets extra options associated with this context.
@@ -262,50 +263,14 @@
         }
 
         /**
-         * Sets the {@link Uri} associated with this context.
-         *
-         * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for an example.
-         *
-         * @param uri URI associated with this context.
-         * @return this builder.
-         *
-         * @throws IllegalStateException if {@link #build()} was already called.
-         */
-        @NonNull
-        public Builder setUri(@NonNull Uri uri) {
-            mUri = Preconditions.checkNotNull(uri);
-            throwIfDestroyed();
-            return this;
-        }
-
-        /**
-         * Sets an {@link Intent#getAction() intent action} associated with this context.
-         *
-         * @param action intent action
-         *
-         * @return this builder
-         *
-         * @throws IllegalStateException if {@link #build()} was already called.
-         */
-        @NonNull
-        public Builder setAction(@NonNull String action) {
-            mAction = Preconditions.checkNotNull(action);
-            throwIfDestroyed();
-            return this;
-        }
-
-        /**
          * Builds the {@link ContentCaptureContext}.
          *
-         * @throws IllegalStateException if {@link #build()} was already called or no call to either
-         * {@link #setExtras(Bundle)}, {@link #setAction(String)}, or {@link #setUri(Uri)} was made.
+         * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return the built {@code ContentCaptureContext}
          */
         public ContentCaptureContext build() {
             throwIfDestroyed();
-            Preconditions.checkState(mExtras != null || mUri != null || mAction != null,
-                    "Must call setUri() or setExtras() or setUri() before calling build()");
             mDestroyed = true;
             return new ContentCaptureContext(this);
         }
@@ -320,7 +285,12 @@
      */
     // TODO(b/111276913): dump to proto as well
     public void dump(PrintWriter pw) {
-        pw.print("comp="); pw.print(ComponentName.flattenToShortString(mComponentName));
+        if (mComponentName != null) {
+            pw.print("activity="); pw.print(mComponentName.flattenToShortString());
+        }
+        if (mId != null) {
+            pw.print(", id="); mId.dump(pw);
+        }
         pw.print(", taskId="); pw.print(mTaskId);
         pw.print(", displayId="); pw.print(mDisplayId);
         if (mParentSessionId != null) {
@@ -333,38 +303,31 @@
             // NOTE: cannot dump because it could contain PII
             pw.print(", hasExtras");
         }
-        if (mUri != null) {
-            // NOTE: cannot dump because it could contain PII
-            pw.print(", hasUri");
-        }
-        if (mAction != null) {
-            // NOTE: cannot dump because it could contain PII
-            pw.print(", hasAction");
-        }
+    }
+
+    private boolean fromServer() {
+        return mComponentName != null;
     }
 
     @Override
     public String toString() {
-        final StringBuilder builder = new StringBuilder("Context[act=")
-                .append(ComponentName.flattenToShortString(mComponentName))
+        final StringBuilder builder = new StringBuilder("Context[");
+
+        if (fromServer()) {
+            builder.append("act=").append(ComponentName.flattenToShortString(mComponentName))
                 .append(", taskId=").append(mTaskId)
                 .append(", displayId=").append(mDisplayId)
                 .append(", flags=").append(mFlags);
+        } else {
+            builder.append("id=").append(mId);
+            if (mExtras != null) {
+                // NOTE: cannot print because it could contain PII
+                builder.append(", hasExtras");
+            }
+        }
         if (mParentSessionId != null) {
             builder.append(", parentId=").append(mParentSessionId);
         }
-        if (mExtras != null) {
-            // NOTE: cannot print because it could contain PII
-            builder.append(", hasExtras");
-        }
-        if (mUri != null) {
-            // NOTE: cannot print because it could contain PII
-            builder.append(", hasUri");
-        }
-        if (mAction != null) {
-            // NOTE: cannot print because it could contain PII
-            builder.append(", hasAction");
-        }
         return builder.append(']').toString();
     }
 
@@ -377,12 +340,11 @@
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mHasClientContext ? 1 : 0);
         if (mHasClientContext) {
-            parcel.writeParcelable(mUri, flags);
-            parcel.writeString(mAction);
+            parcel.writeParcelable(mId, flags);
             parcel.writeBundle(mExtras);
         }
         parcel.writeParcelable(mComponentName, flags);
-        if (mComponentName != null) {
+        if (fromServer()) {
             parcel.writeInt(mTaskId);
             parcel.writeInt(mDisplayId);
             parcel.writeInt(mFlags);
@@ -399,12 +361,9 @@
             final ContentCaptureContext clientContext;
             if (hasClientContext) {
                 // Must reconstruct the client context using the Builder API
-                final Builder builder = new Builder();
-                final Uri uri = parcel.readParcelable(null);
-                final String action = parcel.readString();
+                final LocusId id = parcel.readParcelable(null);
                 final Bundle extras = parcel.readBundle();
-                if (uri != null) builder.setUri(uri);
-                if (action != null) builder.setAction(action);
+                final Builder builder = new Builder(id);
                 if (extras != null) builder.setExtras(extras);
                 clientContext = new ContentCaptureContext(builder);
             } else {
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 22254cd..9cdbefa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -91,13 +91,22 @@
      */
     public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
 
+    /**
+     * Called after a call to
+     * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
+     *
+     * <p>The passed context is available through {@link #getContentCaptureContext()}.
+     */
+    public static final int TYPE_CONTEXT_UPDATED = 6;
+
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_VIEW_APPEARED,
             TYPE_VIEW_DISAPPEARED,
             TYPE_VIEW_TEXT_CHANGED,
             TYPE_INITIAL_VIEW_TREE_APPEARING,
-            TYPE_INITIAL_VIEW_TREE_APPEARED
+            TYPE_INITIAL_VIEW_TREE_APPEARED,
+            TYPE_CONTEXT_UPDATED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -193,12 +202,13 @@
     }
 
     /**
-     * Used by {@link #TYPE_SESSION_STARTED}.
+     * Gets the {@link ContentCaptureContext} set calls to
+     * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
      *
-     * @hide
+     * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events.
      */
     @Nullable
-    public ContentCaptureContext getClientContext() {
+    public ContentCaptureContext getContentCaptureContext() {
         return mClientContext;
     }
 
@@ -220,8 +230,8 @@
      * Gets the type of the event.
      *
      * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
-     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
-     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
+     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
+     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
      */
     public @EventType int getType() {
         return mType;
@@ -299,6 +309,10 @@
         if (mText != null) {
             pw.print(", text="); pw.println(getSanitizedString(mText));
         }
+        if (mClientContext != null) {
+            pw.print(", context="); mClientContext.dump(pw); pw.println();
+
+        }
     }
 
     @Override
@@ -325,6 +339,9 @@
         if (mText != null) {
             string.append(", text=").append(getSanitizedString(mText));
         }
+        if (mClientContext != null) {
+            string.append(", context=").append(mClientContext);
+        }
         return string.append(']').toString();
     }
 
@@ -345,7 +362,7 @@
         if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
             parcel.writeString(mParentSessionId);
         }
-        if (mType == TYPE_SESSION_STARTED) {
+        if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
             parcel.writeParcelable(mClientContext, flags);
         }
     }
@@ -375,7 +392,7 @@
             if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
                 event.setParentSessionId(parcel.readString());
             }
-            if (type == TYPE_SESSION_STARTED) {
+            if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
                 event.setClientContext(parcel.readParcelable(null));
             }
             return event;
@@ -404,6 +421,8 @@
                 return "INITIAL_VIEW_HIERARCHY_STARTED";
             case TYPE_INITIAL_VIEW_TREE_APPEARED:
                 return "INITIAL_VIEW_HIERARCHY_FINISHED";
+            case TYPE_CONTEXT_UPDATED:
+                return "CONTEXT_UPDATED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java
index 508880f..1cf27fc 100644
--- a/core/java/android/view/contentcapture/ContentCaptureHelper.java
+++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java
@@ -15,16 +15,29 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_OFF;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_VERBOSE;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
+import android.provider.DeviceConfig;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureManager.LoggingLevel;
 
 /**
- * Helpe class for this package.
+ * Helper class for this package and server's.
+ *
+ * @hide
  */
-final class ContentCaptureHelper {
+public final class ContentCaptureHelper {
 
-    // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
-    static final boolean VERBOSE = false;
-    static final boolean DEBUG = true; // STOPSHIP if not set to false
+    private static final String TAG = ContentCaptureHelper.class.getSimpleName();
+
+    public static boolean sVerbose = false;
+    public static boolean sDebug = true;
 
     /**
      * Used to log text that could contain PII.
@@ -34,6 +47,61 @@
         return text == null ? null : text.length() + "_chars";
     }
 
+    /**
+     * Gets the value of a device config property from the Content Capture namespace.
+     */
+    public static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) {
+        final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, key);
+        if (value == null) return defaultValue;
+
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Sets the value of the static logging level constants based on device config.
+     */
+    public static void setLoggingLevel() {
+        final int defaultLevel = Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF;
+        final int level = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL,
+                defaultLevel);
+        Log.i(TAG, "Setting logging level to " + getLoggingLevelAsString(level));
+        sVerbose = sDebug = false;
+        switch (level) {
+            case LOGGING_LEVEL_VERBOSE:
+                sVerbose = true;
+                // fall through
+            case LOGGING_LEVEL_DEBUG:
+                sDebug = true;
+                return;
+            case LOGGING_LEVEL_OFF:
+                // You log nothing, Jon Snow!
+                return;
+            default:
+                Log.w(TAG, "setLoggingLevel(): invalud level: " + level);
+        }
+    }
+
+    /**
+     * Gets a user-friendly value for a content capture logging level.
+     */
+    public static String getLoggingLevelAsString(@LoggingLevel int level) {
+        switch (level) {
+            case LOGGING_LEVEL_OFF:
+                return "OFF";
+            case LOGGING_LEVEL_DEBUG:
+                return "DEBUG";
+            case LOGGING_LEVEL_VERBOSE:
+                return "VERBOSE";
+            default:
+                return "UNKNOWN-" + level;
+        }
+    }
+
     private ContentCaptureHelper() {
         throw new UnsupportedOperationException("contains only static methods");
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 634443d..87e358c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -15,9 +15,10 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -38,16 +39,11 @@
 import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
-/*
- * NOTE: all methods in this class should return right away, or do the real work in a handler
- * thread.
- *
- * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
- * of every method.
- */
 /**
- * TODO(b/123577059): add javadocs / implement
+ * TODO(b/123577059): add javadocs / mention it can be null
  */
 @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
 public final class ContentCaptureManager {
@@ -85,12 +81,81 @@
     public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
             "service_explicitly_enabled";
 
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
+
+    /**
+     * Frequency (in ms) of buffer flushes when no events are received.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
+
+    /**
+     * Frequency (in ms) of buffer flushes when no events are received and the last one was a
+     * text change event.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY =
+            "text_change_flush_frequency";
+
+    /**
+     * Size of events that are logging on {@code dump}.
+     *
+     * <p>Set it to {@code 0} or less to disable history.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
+
+    /**
+     * Sets the logging level for {@code logcat} statements.
+     *
+     * <p>Valid values are: {@link #LOGGING_LEVEL_OFF}, {@value #LOGGING_LEVEL_DEBUG}, and
+     * {@link #LOGGING_LEVEL_VERBOSE}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
+
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_OFF = 0;
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_DEBUG = 1;
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_VERBOSE = 2;
+
+    /** @hide */
+    @IntDef(flag = false, value = {
+            LOGGING_LEVEL_OFF,
+            LOGGING_LEVEL_DEBUG,
+            LOGGING_LEVEL_VERBOSE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LoggingLevel {}
+
     private final Object mLock = new Object();
 
     @NonNull
     private final Context mContext;
 
-    @Nullable
+    @NonNull
     private final IContentCaptureManager mService;
 
     // Flags used for starting session.
@@ -107,11 +172,17 @@
 
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
-            @Nullable IContentCaptureManager service) {
+            @NonNull IContentCaptureManager service) {
         mContext = Preconditions.checkNotNull(context, "context cannot be null");
-        if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName());
+        mService = Preconditions.checkNotNull(service, "service cannot be null");
 
-        mService = service;
+        // TODO(b/123096662): right now we're reading the device config values here, but ideally
+        // it should be read on ContentCaptureManagerService and passed back when the activity
+        // started.
+        ContentCaptureHelper.setLoggingLevel();
+
+        if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName());
+
         // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we
         // do, then we should optimize it to run the tests after the Choreographer finishes the most
         // important steps of the frame.
@@ -133,7 +204,7 @@
         synchronized (mLock) {
             if (mMainSession == null) {
                 mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
-                if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
+                if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
             }
             return mMainSession;
         }
@@ -199,8 +270,6 @@
      * </ul>
      */
     public boolean isContentCaptureEnabled() {
-        if (mService == null) return false;
-
         final MainContentCaptureSession mainSession;
         synchronized (mLock) {
             mainSession = mMainSession;
@@ -219,7 +288,7 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
         }
 
@@ -242,8 +311,6 @@
     @SystemApi
     @TestApi
     public boolean isContentCaptureFeatureEnabled() {
-        if (mService == null) return false;
-
         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         final int resultCode;
         try {
@@ -260,39 +327,8 @@
             case RESULT_CODE_NOT_SERVICE:
                 throw new SecurityException("caller is not user's ContentCapture service");
             default:
-                throw new IllegalStateException("received invalid result: " + resultCode);
-        }
-    }
-
-    /**
-     * Sets whether Content Capture is enabled for the given user.
-     *
-     * @throws SecurityException if caller is not the app that owns the Content Capture service
-     * associated with the user.
-     *
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public void setContentCaptureFeatureEnabled(boolean enabled) {
-        if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
-
-        final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
-        final int resultCode;
-        try {
-            mService.setContentCaptureFeatureEnabled(enabled, resultReceiver);
-            resultCode = resultReceiver.getIntResult();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        switch (resultCode) {
-            case RESULT_CODE_TRUE:
-                // Our work is done here, in our void existance...
-                return;
-            case RESULT_CODE_NOT_SERVICE:
-                throw new SecurityException("caller is not user's ContentCapture service");
-            default:
-                throw new IllegalStateException("received invalid result: " + resultCode);
+                Log.wtf(TAG, "received invalid result: " + resultCode);
+                return false;
         }
     }
 
@@ -314,22 +350,23 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("ContentCaptureManager");
+        final String prefix2 = prefix + "  ";
         synchronized (mLock) {
-            pw.print(prefix); pw.println("ContentCaptureManager");
-            pw.print(prefix); pw.print("isContentCaptureEnabled(): ");
+            pw.print(prefix2); pw.print("isContentCaptureEnabled(): ");
             pw.println(isContentCaptureEnabled());
+            pw.print(prefix); pw.print("Debug: "); pw.print(sDebug);
+            pw.print(" Verbose: "); pw.println(sVerbose);
             pw.print(prefix); pw.print("Context: "); pw.println(mContext);
             pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
-            if (mService != null) {
-                pw.print(prefix); pw.print("Service: "); pw.println(mService);
-            }
+            pw.print(prefix); pw.print("Service: "); pw.println(mService);
             pw.print(prefix); pw.print("Flags: "); pw.println(mFlags);
             if (mMainSession != null) {
-                final String prefix2 = prefix + "  ";
-                pw.print(prefix); pw.println("Main session:");
-                mMainSession.dump(prefix2, pw);
+                final String prefix3 = prefix2 + "  ";
+                pw.print(prefix2); pw.println("Main session:");
+                mMainSession.dump(prefix3, pw);
             } else {
-                pw.print(prefix); pw.println("No sessions");
+                pw.print(prefix2); pw.println("No sessions");
             }
         }
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index e028961..1e051a4 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,8 +15,8 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
@@ -120,6 +120,13 @@
      */
     public static final int STATE_INTERNAL_ERROR = 0x100;
 
+    /**
+     * Session is disabled because service didn't whitelist package.
+     *
+     * @hide
+     */
+    public static final int STATE_PACKAGE_NOT_WHITELISTED = 0x200;
+
     private static final int INITIAL_CHILDREN_CAPACITY = 5;
 
     /** @hide */
@@ -166,6 +173,14 @@
     private ContentCaptureSessionId mContentCaptureSessionId;
 
     /**
+     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
+     * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
+     * context.
+     */
+    @Nullable
+    private ContentCaptureContext mClientContext;
+
+    /**
      * List of children session.
      */
     @Nullable
@@ -183,6 +198,12 @@
         mId = Preconditions.checkNotNull(id);
     }
 
+    // Used by ChildCOntentCaptureSession
+    ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
+        this();
+        mClientContext = Preconditions.checkNotNull(initialContext);
+    }
+
     /** @hide */
     @NonNull
     abstract MainContentCaptureSession getMainCaptureSession();
@@ -219,7 +240,7 @@
     public final ContentCaptureSession createContentCaptureSession(
             @NonNull ContentCaptureContext context) {
         final ContentCaptureSession child = newChild(context);
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
                     + child.mId);
         }
@@ -240,6 +261,30 @@
     abstract void flush(@FlushReason int reason);
 
     /**
+     * Sets the {@link ContentCaptureContext} associated with the session.
+     *
+     * <p>Typically used to change the context associated with the default session from an activity.
+     */
+    public final void setContentCaptureContext(@Nullable ContentCaptureContext context) {
+        mClientContext = context;
+        updateContentCaptureContext(context);
+    }
+
+    abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context);
+
+    /**
+     * Gets the {@link ContentCaptureContext} associated with the session.
+     *
+     * @return context set on constructor or by
+     *         {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never
+     *         explicitly set.
+     */
+    @Nullable
+    public final ContentCaptureContext getContentCaptureContext() {
+        return mClientContext;
+    }
+
+    /**
      * Destroys this session, flushing out all pending notifications to the service.
      *
      * <p>Once destroyed, any new notification will be dropped.
@@ -247,20 +292,20 @@
     public final void destroy() {
         synchronized (mLock) {
             if (mDestroyed) {
-                if (DEBUG) Log.d(TAG, "destroy(" + mId + "): already destroyed");
+                if (sDebug) Log.d(TAG, "destroy(" + mId + "): already destroyed");
                 return;
             }
             mDestroyed = true;
 
             // TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
             // id) and send it to the cache of batched commands
-            if (VERBOSE) {
+            if (sVerbose) {
                 Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
             }
             // Finish children first
             if (mChildren != null) {
                 final int numberChildren = mChildren.size();
-                if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
+                if (sVerbose) Log.v(TAG, "Destroying " + numberChildren + " children first");
                 for (int i = 0; i < numberChildren; i++) {
                     final ContentCaptureSession child = mChildren.get(i);
                     try {
@@ -424,6 +469,9 @@
     @CallSuper
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: "); pw.println(mId);
+        if (mClientContext != null) {
+            pw.print(prefix); mClientContext.dump(pw); pw.println();
+        }
         synchronized (mLock) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
             if (mChildren != null && !mChildren.isEmpty()) {
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 26cf34c..e3b0372 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -67,9 +67,4 @@
      * Returns whether the content capture feature is enabled for the calling user.
      */
     void isContentCaptureFeatureEnabled(in IResultReceiver result);
-
-    /**
-     * Sets whether the content capture feature is enabled for the given user.
-     */
-    void setContentCaptureFeatureEnabled(boolean enabled, in IResultReceiver result);
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 810c967..f4021b1 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -15,6 +15,7 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
@@ -22,9 +23,13 @@
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.getIntDeviceConfigProperty;
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -63,6 +68,7 @@
 
     private static final String TAG = MainContentCaptureSession.class.getSimpleName();
 
+    // For readability purposes...
     private static final boolean FORCE_FLUSH = true;
 
     /**
@@ -70,17 +76,9 @@
      */
     private static final int MSG_FLUSH = 1;
 
-    /**
-     * Maximum number of events that are buffered before sent to the app.
-     */
-    // TODO(b/121044064): use settings
-    private static final int MAX_BUFFER_SIZE = 100;
-
-    /**
-     * Frequency the buffer is flushed if stale.
-     */
-    // TODO(b/121044064): use settings
-    private static final int FLUSHING_FREQUENCY_MS = 5_000;
+    private static final int DEFAULT_MAX_BUFFER_SIZE = 100;
+    private static final int DEFAULT_FLUSHING_FREQUENCY_MS = 5_000;
+    private static final int DEFAULT_LOG_HISTORY_SIZE = 10;
 
     /**
      * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
@@ -104,14 +102,14 @@
      * Interface to the system_server binder object - it's only used to start the session (and
      * notify when the session is finished).
      */
-    @Nullable // TODO(b/122959591): shoul never be null, we should make main session null instead
+    @NonNull
     private final IContentCaptureManager mSystemServerInterface;
 
     /**
      * Direct interface to the service binder object - it's used to send the events, including the
      * last ones (when the session is finished)
      */
-    @Nullable
+    @NonNull
     private IContentCaptureDirectManager mDirectServiceInterface;
     @Nullable
     private DeathRecipient mDirectServiceVulture;
@@ -130,20 +128,42 @@
     @Nullable
     private ArrayList<ContentCaptureEvent> mEvents;
 
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     */
+    private final int mMaxBufferSize;
+
+    /**
+     * Frequency the buffer is flushed if idle.
+     */
+    private final int mIdleFlushingFrequencyMs;
+
     // Used just for debugging purposes (on dump)
     private long mNextFlush;
 
-    // TODO(b/121044064): use settings to set size
-    private final LocalLog mFlushHistory = new LocalLog(10);
+    @Nullable
+    private final LocalLog mFlushHistory;
 
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context,
             @NonNull ContentCaptureManager manager, @NonNull Handler handler,
-            @Nullable IContentCaptureManager systemServerInterface) {
+            @NonNull IContentCaptureManager systemServerInterface) {
         mContext = context;
         mManager = manager;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
+
+        // TODO(b/123096662): right now we're reading the device config values here, but ideally
+        // it should be read on ContentCaptureManagerService and passed back when the activity
+        // started.
+        mMaxBufferSize = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
+                DEFAULT_MAX_BUFFER_SIZE);
+        mIdleFlushingFrequencyMs = getIntDeviceConfigProperty(
+                DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, DEFAULT_FLUSHING_FREQUENCY_MS);
+        final int logHistorySize = getIntDeviceConfigProperty(
+                DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, DEFAULT_LOG_HISTORY_SIZE);
+
+        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
     }
 
     @Override
@@ -168,14 +188,14 @@
             int flags) {
         if (!isContentCaptureEnabled()) return;
 
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "start(): token=" + token + ", comp="
                     + ComponentName.flattenToShortString(component));
         }
 
         if (hasStarted()) {
             // TODO(b/122959591): make sure this is expected (and when), or use Log.w
-            if (DEBUG) {
+            if (sDebug) {
                 Log.d(TAG, "ignoring handleStartSession(" + token + "/"
                         + ComponentName.flattenToShortString(component) + " while on state "
                         + getStateAsString(mState));
@@ -186,14 +206,12 @@
         mApplicationToken = token;
         mComponentName = component;
 
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleStartSession(): token=" + token + ", act="
                     + getDebugState() + ", id=" + mId);
         }
 
         try {
-            if (mSystemServerInterface == null) return;
-
             mSystemServerInterface.startSession(mApplicationToken, component, mId, flags,
                     new IResultReceiver.Stub() {
                         @Override
@@ -253,7 +271,7 @@
             mState = resultCode;
             mDisabled.set(false);
         }
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                     + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
@@ -268,26 +286,27 @@
     @UiThread
     private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
         final int eventType = event.getType();
-        if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
-        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+        if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
+        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
+                && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
             // TODO(b/120494182): comment when this could happen (dialogs?)
             Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
                     + ContentCaptureEvent.getTypeAsString(eventType)
-                    + "): session not started yet");
+                    + "): dropping because session not started yet");
             return;
         }
         if (mDisabled.get()) {
             // This happens when the event was queued in the handler before the sesison was ready,
             // then handleSessionStarted() returned and set it as disabled - we need to drop it,
             // otherwise it will keep triggering handleScheduleFlush()
-            if (VERBOSE) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
+            if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
             return;
         }
         if (mEvents == null) {
-            if (VERBOSE) {
-                Log.v(TAG, "handleSendEvent(): creating buffer for " + MAX_BUFFER_SIZE + " events");
+            if (sVerbose) {
+                Log.v(TAG, "handleSendEvent(): creating buffer for " + mMaxBufferSize + " events");
             }
-            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+            mEvents = new ArrayList<>(mMaxBufferSize);
         }
 
         // Some type of events can be merged together
@@ -299,7 +318,7 @@
             // TODO(b/121045053): check if flags match
             if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
                     && lastEvent.getId().equals(event.getId())) {
-                if (VERBOSE) {
+                if (sVerbose) {
                     Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
                             + getSanitizedString(event.getText()));
                 }
@@ -313,7 +332,7 @@
             final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
             if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
                     && event.getSessionId().equals(lastEvent.getSessionId())) {
-                if (VERBOSE) {
+                if (sVerbose) {
                     Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
                             + lastEvent.getSessionId());
                 }
@@ -328,20 +347,20 @@
 
         final int numberEvents = mEvents.size();
 
-        final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+        final boolean bufferEvent = numberEvents < mMaxBufferSize;
 
         if (bufferEvent && !forceFlush) {
             scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
             return;
         }
 
-        if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
+        if (mState != STATE_ACTIVE && numberEvents >= mMaxBufferSize) {
             // Callback from startSession hasn't been called yet - typically happens on system
             // apps that are started before the system service
             // TODO(b/122959591): try to ignore session while system is not ready / boot
             // not complete instead. Similarly, the manager service should return right away
             // when the user does not have a service set
-            if (DEBUG) {
+            if (sDebug) {
                 Log.d(TAG, "Closing session for " + getDebugState()
                         + " after " + numberEvents + " delayed events");
             }
@@ -396,12 +415,12 @@
 
     @UiThread
     private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
                     + ", checkExisting=" + checkExisting);
         }
         if (!hasStarted()) {
-            if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet");
+            if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
             return;
         }
 
@@ -416,19 +435,19 @@
             // "Renew" the flush message by removing the previous one
             mHandler.removeMessages(MSG_FLUSH);
         }
-        mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS;
-        if (VERBOSE) {
+        mNextFlush = System.currentTimeMillis() + mIdleFlushingFrequencyMs;
+        if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
-                    + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
+                    + mIdleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
         }
         // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
-        mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, FLUSHING_FREQUENCY_MS);
+        mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, mIdleFlushingFrequencyMs);
     }
 
     @UiThread
     private void flushIfNeeded(@FlushReason int reason) {
         if (mEvents == null || mEvents.isEmpty()) {
-            if (VERBOSE) Log.v(TAG, "Nothing to flush");
+            if (sVerbose) Log.v(TAG, "Nothing to flush");
             return;
         }
         flush(reason);
@@ -446,7 +465,7 @@
         }
 
         if (mDirectServiceInterface == null) {
-            if (VERBOSE) {
+            if (sVerbose) {
                 Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
                         + "client not ready: " + mEvents);
             }
@@ -458,14 +477,16 @@
 
         final int numberEvents = mEvents.size();
         final String reasonString = getflushReasonAsString(reason);
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
         }
-        // Logs reason, size, max size, idle timeout
-        final String logRecord = "r=" + reasonString + " s=" + numberEvents
-                + " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS;
-        try {
+        if (mFlushHistory != null) {
+            // Logs reason, size, max size, idle timeout
+            final String logRecord = "r=" + reasonString + " s=" + numberEvents
+                    + " m=" + mMaxBufferSize + " i=" + mIdleFlushingFrequencyMs;
             mFlushHistory.log(logRecord);
+        }
+        try {
             mHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
@@ -476,6 +497,11 @@
         }
     }
 
+    @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        notifyContextUpdated(mId, context);
+    }
+
     /**
      * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
      */
@@ -493,15 +519,13 @@
 
     @UiThread
     private void destroySession() {
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
                     + getDebugState());
         }
 
         try {
-            if (mSystemServerInterface == null) return;
-
             mSystemServerInterface.finishSession(mId);
         } catch (RemoteException e) {
             Log.e(TAG, "Error destroying system-service session " + mId + " for "
@@ -513,7 +537,7 @@
     // clearings out.
     @UiThread
     private void resetSession(int newState) {
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
                     + getStateAsString(mState) + " to " + getStateAsString(newState));
         }
@@ -613,14 +637,19 @@
         }
     }
 
+    void notifyContextUpdated(@NonNull String sessionId,
+            @Nullable ContentCaptureContext context) {
+        sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+                .setClientContext(context));
+    }
+
     @Override
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        super.dump(prefix, pw);
+
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
         pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
-        if (mSystemServerInterface != null) {
-            pw.print(prefix); pw.print("mSystemServerInterface: ");
-            pw.println(mSystemServerInterface);
-        }
+        pw.print(prefix); pw.print("mSystemServerInterface: ");
         if (mDirectServiceInterface != null) {
             pw.print(prefix); pw.print("mDirectServiceInterface: ");
             pw.println(mDirectServiceInterface);
@@ -638,8 +667,8 @@
         if (mEvents != null && !mEvents.isEmpty()) {
             final int numberEvents = mEvents.size();
             pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
-            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
-            if (VERBOSE && numberEvents > 0) {
+            pw.print('/'); pw.println(mMaxBufferSize);
+            if (sVerbose && numberEvents > 0) {
                 final String prefix3 = prefix + "  ";
                 for (int i = 0; i < numberEvents; i++) {
                     final ContentCaptureEvent event = mEvents.get(i);
@@ -647,13 +676,17 @@
                     pw.println();
                 }
             }
-            pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+            pw.print(prefix); pw.print("flush frequency: "); pw.println(mIdleFlushingFrequencyMs);
             pw.print(prefix); pw.print("next flush: ");
             TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
             pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
         }
-        pw.print(prefix); pw.println("flush history:");
-        mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+        if (mFlushHistory != null) {
+            pw.print(prefix); pw.println("flush history:");
+            mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+        } else {
+            pw.print(prefix); pw.println("not logging flush history");
+        }
 
         super.dump(prefix, pw);
     }
diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
index 8fedcd5..7d66af9 100644
--- a/core/java/android/view/contentcapture/UserDataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
@@ -17,7 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityThread;
-import android.net.Uri;
+import android.content.LocusId;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.IntArray;
@@ -36,16 +36,16 @@
     private final String mPackageName;
 
     private final boolean mForEverything;
-    private ArrayList<UriRequest> mUriRequests;
+    private ArrayList<LocusIdRequest> mLocusIdRequests;
 
     private UserDataRemovalRequest(@NonNull Builder builder) {
         mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
         mForEverything = builder.mForEverything;
-        if (builder.mUris != null) {
-            final int size = builder.mUris.size();
-            mUriRequests = new ArrayList<>(size);
+        if (builder.mLocusIds != null) {
+            final int size = builder.mLocusIds.size();
+            mLocusIdRequests = new ArrayList<>(size);
             for (int i = 0; i < size; i++) {
-                mUriRequests.add(new UriRequest(builder.mUris.get(i),
+                mLocusIdRequests.add(new LocusIdRequest(builder.mLocusIds.get(i),
                         builder.mRecursive.get(i) == 1));
             }
         }
@@ -56,9 +56,9 @@
         mForEverything = parcel.readBoolean();
         if (!mForEverything) {
             final int size = parcel.readInt();
-            mUriRequests = new ArrayList<>(size);
+            mLocusIdRequests = new ArrayList<>(size);
             for (int i = 0; i < size; i++) {
-                mUriRequests.add(new UriRequest((Uri) parcel.readValue(null),
+                mLocusIdRequests.add(new LocusIdRequest((LocusId) parcel.readValue(null),
                         parcel.readBoolean()));
             }
         }
@@ -80,11 +80,11 @@
     }
 
     /**
-     * Gets the list of {@code Uri}s the apps is requesting to remove.
+     * Gets the list of {@code LousId}s the apps is requesting to remove.
      */
     @NonNull
-    public List<UriRequest> getUriRequests() {
-        return mUriRequests;
+    public List<LocusIdRequest> getLocusIdRequests() {
+        return mLocusIdRequests;
     }
 
     /**
@@ -93,7 +93,7 @@
     public static final class Builder {
 
         private boolean mForEverything;
-        private ArrayList<Uri> mUris;
+        private ArrayList<LocusId> mLocusIds;
         private IntArray mRecursive;
 
         private boolean mDestroyed;
@@ -106,36 +106,32 @@
         @NonNull
         public Builder forEverything() {
             throwIfDestroyed();
-            if (mUris != null) {
-                throw new IllegalStateException("Already added Uris");
-            }
+            Preconditions.checkState(mLocusIds == null, "Already added LocusIds");
 
             mForEverything = true;
             return this;
         }
 
         /**
-         * Request service to remove data associated with a given {@link Uri}.
+         * Request service to remove data associated with a given {@link LocusId}.
          *
-         * @param uri URI being requested to be removed.
-         * @param recursive whether it should remove the data associated with just the URI or its
-         * tree of descendants.
+         * @param locusId the {@link LocusId} being requested to be removed.
+         * @param recursive whether it should remove the data associated with just the
+         * {@code LocusId} or its tree of descendants.
          *
          * @return this builder
          */
-        public Builder addUri(@NonNull Uri uri, boolean recursive) {
+        public Builder addLocusId(@NonNull LocusId locusId, boolean recursive) {
             throwIfDestroyed();
-            if (mForEverything) {
-                throw new IllegalStateException("Already is for everything");
-            }
-            Preconditions.checkNotNull(uri);
+            Preconditions.checkState(!mForEverything, "Already is for everything");
+            Preconditions.checkNotNull(locusId);
 
-            if (mUris == null) {
-                mUris = new ArrayList<>();
+            if (mLocusIds == null) {
+                mLocusIds = new ArrayList<>();
                 mRecursive = new IntArray();
             }
 
-            mUris.add(uri);
+            mLocusIds.add(locusId);
             mRecursive.add(recursive ? 1 : 0);
             return this;
         }
@@ -147,7 +143,7 @@
         public UserDataRemovalRequest build() {
             throwIfDestroyed();
 
-            Preconditions.checkState(mForEverything || mUris != null);
+            Preconditions.checkState(mForEverything || mLocusIds != null);
 
             mDestroyed = true;
             return new UserDataRemovalRequest(this);
@@ -168,11 +164,11 @@
         parcel.writeString(mPackageName);
         parcel.writeBoolean(mForEverything);
         if (!mForEverything) {
-            final int size = mUriRequests.size();
+            final int size = mLocusIdRequests.size();
             parcel.writeInt(size);
             for (int i = 0; i < size; i++) {
-                final UriRequest request = mUriRequests.get(i);
-                parcel.writeValue(request.getUri());
+                final LocusIdRequest request = mLocusIdRequests.get(i);
+                parcel.writeValue(request.getLocusId());
                 parcel.writeBoolean(request.isRecursive());
             }
         }
@@ -193,28 +189,28 @@
     };
 
     /**
-     * Representation of a request to remove data associated with an {@link Uri}.
+     * Representation of a request to remove data associated with a {@link LocusId}.
      */
-    public final class UriRequest {
-        private final @NonNull Uri mUri;
+    public final class LocusIdRequest {
+        private final @NonNull LocusId mLocusId;
         private final boolean mRecursive;
 
-        private UriRequest(@NonNull Uri uri, boolean recursive) {
-            this.mUri = uri;
+        private LocusIdRequest(@NonNull LocusId locusId, boolean recursive) {
+            this.mLocusId = locusId;
             this.mRecursive = recursive;
         }
 
         /**
-         * Gets the URI per se.
+         * Gets the {@code LocusId} per se.
          */
         @NonNull
-        public Uri getUri() {
-            return mUri;
+        public LocusId getLocusId() {
+            return mLocusId;
         }
 
         /**
-         * Checks whether the request is to remove just the data associated with the URI per se, or
-         * also its descendants.
+         * Checks whether the request is to remove just the data associated with the {@link LocusId}
+         *  per se, or also its descendants.
          */
         @NonNull
         public boolean isRecursive() {
diff --git a/core/java/android/view/inspector/WindowInspector.java b/core/java/android/view/inspector/WindowInspector.java
new file mode 100644
index 0000000..69d004e
--- /dev/null
+++ b/core/java/android/view/inspector/WindowInspector.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.inspector;
+
+import android.annotation.NonNull;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import java.util.List;
+
+/**
+ * Provides access to window inspection information.
+ */
+public final class WindowInspector {
+    private WindowInspector() {
+        // Non-instantiable.
+    }
+
+    /**
+     * @return the list of all window views attached to the current process
+     */
+    @NonNull
+    public static List<View> getGlobalWindowViews() {
+        return WindowManagerGlobal.getInstance().getWindowViews();
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2171fc5..3555822 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -760,7 +760,7 @@
      * <p>
      * The {@code encoding} parameter specifies whether the data is base64 or URL
      * encoded. If the data is base64 encoded, the value of the encoding
-     * parameter must be 'base64'. HTML can be encoded with {@link
+     * parameter must be {@code "base64"}. HTML can be encoded with {@link
      * android.util.Base64#encodeToString(byte[],int)} like so:
      * <pre>
      * String unencodedHtml =
@@ -768,11 +768,15 @@
      * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
      * webView.loadData(encodedHtml, "text/html", "base64");
      * </pre>
-     * <p>
+     * <p class="note">
      * For all other values of {@code encoding} (including {@code null}) it is assumed that the
      * data uses ASCII encoding for octets inside the range of safe URL characters and use the
      * standard %xx hex encoding of URLs for octets outside that range. See <a
      * href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information.
+     * Applications targeting {@link android.os.Build.VERSION_CODES#Q} or later must either use
+     * base64 or encode any {@code #} characters in the content as {@code %23}, otherwise they
+     * will be treated as the end of the content and the remaining text used as a document
+     * fragment identifier.
      * <p>
      * The {@code mimeType} parameter specifies the format of the data.
      * If WebView can't handle the specified MIME type, it will download the data.
@@ -820,7 +824,8 @@
      * <p>
      * If the base URL uses the data scheme, this method is equivalent to
      * calling {@link #loadData(String,String,String) loadData()} and the
-     * historyUrl is ignored, and the data will be treated as part of a data: URL.
+     * historyUrl is ignored, and the data will be treated as part of a data: URL,
+     * including the requirement that the content be URL-encoded or base64 encoded.
      * If the base URL uses any other scheme, then the data will be loaded into
      * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
      * entities in the string will not be decoded.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4b7c393..f01babe 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -325,13 +325,13 @@
     /**
      * The current position of the selector in the list.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mSelectorPosition = INVALID_POSITION;
 
     /**
      * Defines the selector's location and dimension at drawing time
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Rect mSelectorRect = new Rect();
 
     /**
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 904a862..89e205c 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1230,9 +1230,16 @@
      * Ensures that the drop down is not obscuring the IME.
      * @param visible whether the ime should be in front. If false, the ime is pushed to
      * the background.
+     *
+     * This method is deprecated. Please use the following methods instead.
+     * Use {@link #setInputMethodMode} to ensure that the drop down is not obscuring the IME.
+     * Use {@link #showDropDown()} to show the drop down immediately
+     * A combination of {@link #isDropDownAlwaysVisible()} and {@link #enoughToFilter()} to decide
+     * whether to manually trigger {@link #showDropDown()} or not.
+     *
      * @hide internal used only here and SearchDialog
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768913)
     public void ensureImeVisible(boolean visible) {
         mPopup.setInputMethodMode(visible
                 ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -1242,14 +1249,39 @@
     }
 
     /**
-     * @hide internal used only here and SearchDialog
+     * This method is deprecated. Please use {@link #getInputMethodMode()} instead.
+     *
+     * @hide This API is not being used and can be removed.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean isInputMethodNotNeeded() {
         return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
     }
 
     /**
+     * Returns the input method mode used by the auto complete dropdown.
+     */
+    public int getInputMethodMode() {
+        return mPopup.getInputMethodMode();
+    }
+
+    /**
+     * Use this method to specify when the IME should be displayed. This function can be used to
+     * prevent the dropdown from obscuring the IME.
+     *
+     * @param mode speficies the input method mode. use one of the following values:
+     *
+     * {@link ListPopupWindow#INPUT_METHOD_FROM_FOCUSABLE} IME Displayed if the auto-complete box is
+     * focusable.
+     * {@link ListPopupWindow#INPUT_METHOD_NEEDED} Always display the IME.
+     * {@link ListPopupWindow#INPUT_METHOD_NOT_NEEDED}. The auto-complete suggestions are always
+     * displayed, even if the suggestions cover/hide the input method.
+     */
+    public void setInputMethodMode(int mode) {
+        mPopup.setInputMethodMode(mode);
+    }
+
+    /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 9c21ba6..ded3be4e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6020,9 +6020,6 @@
                     }
 
                     updateSelection(event);
-                    if (mTextView.hasSelection() && mEndHandle != null) {
-                        mEndHandle.updateMagnifier(event);
-                    }
                     break;
 
                 case MotionEvent.ACTION_UP:
@@ -6030,9 +6027,6 @@
                         break;
                     }
                     updateSelection(event);
-                    if (mEndHandle != null) {
-                        mEndHandle.dismissMagnifier();
-                    }
 
                     // No longer dragging to select text, let the parent intercept events.
                     mTextView.getParent().requestDisallowInterceptTouchEvent(false);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 29f070e..8113b40 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -199,7 +199,9 @@
     private boolean mMaxInitialized;
 
     private int mBehavior;
-    @UnsupportedAppUsage
+    // Better to define a Drawable that implements Animatable if you want to modify animation
+    // characteristics programatically.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713)
     private int mDuration;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mIndeterminate;
@@ -284,7 +286,6 @@
             }
         }
 
-
         mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
 
         mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
@@ -709,7 +710,17 @@
     /**
      * Define the drawable used to draw the progress bar in indeterminate mode.
      *
+     * <p>For the Drawable to animate, it must implement {@link Animatable}, or override
+     * {@link Drawable#onLevelChange(int)}.  A Drawable that implements Animatable will be animated
+     * via that interface and therefore provides the greatest amount of customization. A Drawable
+     * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the
+     * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration},
+         * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and
+     * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the
+     * indeterminate animation begins.
+     *
      * @param d the new drawable
+     * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
      * @see #getIndeterminateDrawable()
      * @see #setIndeterminate(boolean)
      */
@@ -1774,10 +1785,21 @@
 
     /**
      * Sets the acceleration curve for the indeterminate animation.
-     * The interpolator is loaded as a resource from the specified context.
+     *
+     * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear
+     * interpolation.
+     *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have an affect.
      *
      * @param context The application environment
      * @param resID The resource identifier of the interpolator to load
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Interpolator)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Context context, @InterpolatorRes int resID) {
         setInterpolator(AnimationUtils.loadInterpolator(context, resID));
@@ -1787,7 +1809,17 @@
      * Sets the acceleration curve for the indeterminate animation.
      * Defaults to a linear interpolation.
      *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have
+     * an affect.
+     *
      * @param interpolator The interpolator which defines the acceleration curve
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Interpolator interpolator) {
         mInterpolator = interpolator;
@@ -1797,6 +1829,9 @@
      * Gets the acceleration curve type for the indeterminate animation.
      *
      * @return the {@link Interpolator} associated to this animation
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #setInterpolator(Interpolator)
      */
     @InspectableProperty
     public Interpolator getInterpolator() {
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index e91f87e..1bed32ec 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -17,12 +17,15 @@
 package android.widget;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
 
 import com.android.internal.widget.ScrollBarUtils;
 
@@ -36,7 +39,7 @@
 public class ScrollBarDrawable extends Drawable implements Drawable.Callback {
     private Drawable mVerticalTrack;
     private Drawable mHorizontalTrack;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768422)
     private Drawable mVerticalThumb;
     private Drawable mHorizontalThumb;
 
@@ -226,7 +229,10 @@
         }
     }
 
-    @UnsupportedAppUsage
+    /**
+     * @see android.view.View#setVerticalThumbDrawable(Drawable)
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void setVerticalThumbDrawable(Drawable thumb) {
         if (mVerticalThumb != null) {
             mVerticalThumb.setCallback(null);
@@ -236,6 +242,37 @@
         mVerticalThumb = thumb;
     }
 
+    /**
+     * @see View#getVerticalTrackDrawable()
+     */
+    public @Nullable Drawable getVerticalTrackDrawable() {
+        return mVerticalTrack;
+    }
+
+    /**
+     * @see View#getVerticalThumbDrawable()
+     */
+    public @Nullable Drawable getVerticalThumbDrawable() {
+        return mVerticalThumb;
+    }
+
+    /**
+     * @see View#getHorizontalTrackDrawable()
+     */
+    public @Nullable Drawable getHorizontalTrackDrawable() {
+        return mHorizontalTrack;
+    }
+
+    /**
+     * @see View#getHorizontalThumbDrawable()
+     */
+    public @Nullable Drawable getHorizontalThumbDrawable() {
+        return mHorizontalThumb;
+    }
+
+    /**
+     * @see android.view.View#setVerticalTrackDrawable(Drawable)
+     */
     public void setVerticalTrackDrawable(Drawable track) {
         if (mVerticalTrack != null) {
             mVerticalTrack.setCallback(null);
@@ -245,7 +282,10 @@
         mVerticalTrack = track;
     }
 
-    @UnsupportedAppUsage
+    /**
+     * @see android.view.View#setHorizontalThumbDrawable(Drawable)
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void setHorizontalThumbDrawable(Drawable thumb) {
         if (mHorizontalThumb != null) {
             mHorizontalThumb.setCallback(null);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 630c38a..172f1d8 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -2078,6 +2078,11 @@
             return ic;
         }
 
+        @Override
+        public boolean checkInputConnectionProxy(View view) {
+            return view == mSearchView;
+        }
+
         private void showSoftInputIfNecessary() {
             if (mHasPendingShowSoftInputRequest) {
                 final InputMethodManager imm =
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index eef40e1..d037337 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -95,7 +95,7 @@
     public static final int LENGTH_LONG = 1;
 
     final Context mContext;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     final TN mTN;
     @UnsupportedAppUsage
     int mDuration;
@@ -354,7 +354,7 @@
     }
 
     private static class TN extends ITransientNotification.Stub {
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
 
         private static final int SHOW = 0;
@@ -362,18 +362,18 @@
         private static final int CANCEL = 2;
         final Handler mHandler;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mGravity;
         int mX;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mY;
         float mHorizontalMargin;
         float mVerticalMargin;
 
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mView;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mNextView;
         int mDuration;
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 119a015..0fbd4dc 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -46,6 +46,8 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
+import android.content.res.Configuration;
+import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -69,6 +71,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.provider.DocumentsContract;
+import android.provider.Downloads;
+import android.provider.OpenableColumns;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -87,7 +92,6 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.AbsListView;
 import android.widget.BaseAdapter;
-import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
@@ -373,50 +377,6 @@
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
 
-        Button copyButton = findViewById(R.id.copy_button);
-        copyButton.setOnClickListener(view -> {
-            Intent targetIntent = getTargetIntent();
-            if (targetIntent == null) {
-                finish();
-            } else {
-                final String action = targetIntent.getAction();
-
-                ClipData clipData = null;
-                if (Intent.ACTION_SEND.equals(action)) {
-                    String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
-                    Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-
-                    if (extraText != null) {
-                        clipData = ClipData.newPlainText(null, extraText);
-                    } else if (extraStream != null) {
-                        clipData = ClipData.newUri(getContentResolver(), null, extraStream);
-                    } else {
-                        Log.w(TAG, "No data available to copy to clipboard");
-                        return;
-                    }
-                } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
-                    final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
-                            Intent.EXTRA_STREAM);
-                    clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
-                    for (int i = 1; i < streams.size(); i++) {
-                        clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
-                    }
-                } else {
-                    // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
-                    // so warn about unexpected action
-                    Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
-                    return;
-                }
-
-                ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
-                        Context.CLIPBOARD_SERVICE);
-                clipboardManager.setPrimaryClip(clipData);
-                Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
-
-                finish();
-            }
-        });
-
         mChooserShownTime = System.currentTimeMillis();
         final long systemCost = mChooserShownTime - intentReceivedTime;
 
@@ -474,6 +434,10 @@
             return;
         }
 
+        if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
+            return;
+        }
+
         int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
 
         getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
@@ -481,6 +445,70 @@
         displayContentPreview(previewType, targetIntent);
     }
 
+    private void onCopyButtonClicked(View v) {
+        Intent targetIntent = getTargetIntent();
+        if (targetIntent == null) {
+            finish();
+        } else {
+            final String action = targetIntent.getAction();
+
+            ClipData clipData = null;
+            if (Intent.ACTION_SEND.equals(action)) {
+                String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
+                Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+
+                if (extraText != null) {
+                    clipData = ClipData.newPlainText(null, extraText);
+                } else if (extraStream != null) {
+                    clipData = ClipData.newUri(getContentResolver(), null, extraStream);
+                } else {
+                    Log.w(TAG, "No data available to copy to clipboard");
+                    return;
+                }
+            } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+                final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
+                        Intent.EXTRA_STREAM);
+                clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
+                for (int i = 1; i < streams.size(); i++) {
+                    clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
+                }
+            } else {
+                // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
+                // so warn about unexpected action
+                Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
+                return;
+            }
+
+            ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
+                    Context.CLIPBOARD_SERVICE);
+            clipboardManager.setPrimaryClip(clipData);
+            Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
+
+            finish();
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        int width = -1;
+        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
+        }
+
+        updateLayoutWidth(R.id.content_preview_text_layout, width);
+        updateLayoutWidth(R.id.content_preview_title_layout, width);
+        updateLayoutWidth(R.id.content_preview_file_layout, width);
+    }
+
+    private void updateLayoutWidth(int layoutResourceId, int width) {
+        View view = findViewById(layoutResourceId);
+        LayoutParams params = view.getLayoutParams();
+        params.width = width;
+        view.setLayoutParams(params);
+    }
+
     private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) {
         switch (previewType) {
             case CONTENT_PREVIEW_TEXT:
@@ -501,6 +529,8 @@
         ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_text_area);
         contentPreviewLayout.setVisibility(View.VISIBLE);
 
+        findViewById(R.id.copy_button).setOnClickListener(this::onCopyButtonClicked);
+
         CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
         if (sharingText == null) {
             findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE);
@@ -510,7 +540,7 @@
         }
 
         String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
-        if (previewTitle == null || previewTitle.trim().isEmpty()) {
+        if (TextUtils.isEmpty(previewTitle)) {
             findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE);
         } else {
             TextView previewTitleView = findViewById(R.id.content_preview_title);
@@ -561,6 +591,7 @@
             if (imageUris.size() == 0) {
                 Log.i(TAG, "Attempted to display image preview area with zero"
                         + " available images detected in EXTRA_STREAM list");
+                contentPreviewLayout.setVisibility(View.GONE);
                 return;
             }
 
@@ -580,15 +611,124 @@
         }
     }
 
+    private static class FileInfo {
+        public final String name;
+        public final boolean hasThumbnail;
+
+        FileInfo(String name, boolean hasThumbnail) {
+            this.name = name;
+            this.hasThumbnail = hasThumbnail;
+        }
+    }
+
+    /**
+     * Wrapping the ContentResolver call to expose for easier mocking,
+     * and to avoid mocking Android core classes.
+     */
+    @VisibleForTesting
+    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
+        return resolver.query(uri, null, null, null, null);
+    }
+
+    private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
+        String fileName = null;
+        boolean hasThumbnail = false;
+
+        try (Cursor cursor = queryResolver(resolver, uri)) {
+            if (cursor != null && cursor.getCount() > 0) {
+                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+                int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);
+                int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
+
+                cursor.moveToFirst();
+                if (nameIndex != -1) {
+                    fileName = cursor.getString(nameIndex);
+                } else if (titleIndex != -1) {
+                    fileName = cursor.getString(titleIndex);
+                }
+
+                if (flagsIndex != -1) {
+                    hasThumbnail = (cursor.getInt(flagsIndex)
+                            & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
+                }
+            }
+        } catch (SecurityException e) {
+            Log.w(TAG, "Error loading file preview", e);
+        }
+
+        if (TextUtils.isEmpty(fileName)) {
+            fileName = uri.getPath();
+            int index = fileName.lastIndexOf('/');
+            if (index != -1) {
+                fileName = fileName.substring(index + 1);
+            }
+        }
+
+        return new FileInfo(fileName, hasThumbnail);
+    }
+
     private void displayFileContentPreview(Intent targetIntent) {
-        // support coming
+        ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_file_area);
+        contentPreviewLayout.setVisibility(View.VISIBLE);
+
+        // TODO(b/120417119): Disable file copy until after moving to sysui,
+        // due to permissions issues
+        findViewById(R.id.file_copy_button).setVisibility(View.GONE);
+
+        String action = targetIntent.getAction();
+        if (Intent.ACTION_SEND.equals(action)) {
+            Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+            loadFileUriIntoView(uri);
+        } else {
+            List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+            int uriCount = uris.size();
+
+            if (uriCount == 0) {
+                contentPreviewLayout.setVisibility(View.GONE);
+                Log.i(TAG,
+                        "Appears to be no uris available in EXTRA_STREAM, removing "
+                                + "preview area");
+                return;
+            } else if (uriCount == 1) {
+                loadFileUriIntoView(uris.get(0));
+            } else {
+                FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver());
+                int remUriCount = uriCount - 1;
+                String fileName = getResources().getQuantityString(R.plurals.file_count,
+                        remUriCount, fileInfo.name, remUriCount);
+
+                TextView fileNameView = findViewById(R.id.content_preview_filename);
+                fileNameView.setText(fileName);
+
+                ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
+                fileIconView.setVisibility(View.VISIBLE);
+                fileIconView.setImageResource(R.drawable.ic_file_copy);
+            }
+        }
+    }
+
+    private void loadFileUriIntoView(Uri uri) {
+        FileInfo fileInfo = extractFileInfo(uri, getContentResolver());
+
+        TextView fileNameView = findViewById(R.id.content_preview_filename);
+        fileNameView.setText(fileInfo.name);
+
+        if (fileInfo.hasThumbnail) {
+            loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
+        } else {
+            ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
+            fileIconView.setVisibility(View.VISIBLE);
+            fileIconView.setImageResource(R.drawable.ic_doc_generic);
+        }
     }
 
     private RoundedRectImageView loadUriIntoView(int imageResourceId, Uri uri) {
         RoundedRectImageView imageView = findViewById(imageResourceId);
-        imageView.setVisibility(View.VISIBLE);
         Bitmap bmp = loadThumbnail(uri, new Size(200, 200));
-        imageView.setImageBitmap(bmp);
+        if (bmp != null) {
+            imageView.setVisibility(View.VISIBLE);
+            imageView.setImageBitmap(bmp);
+        }
 
         return imageView;
     }
@@ -1261,9 +1401,8 @@
         }
 
         try {
-            return ImageUtils.decodeSampledBitmapFromStream(getContentResolver(),
-                uri, size.getWidth(), size.getHeight());
-        } catch (IOException | NullPointerException ex) {
+            return ImageUtils.loadThumbnail(getContentResolver(), uri, size);
+        } catch (IOException | NullPointerException | SecurityException ex) {
             Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex);
         }
         return null;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index c4ab91f..8a90cad 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -44,9 +44,9 @@
     int checkPackage(int uid, String packageName);
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
-    void getHistoricalOps(int uid, String packageName, in String[] ops, long beginTimeMillis,
+    void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis,
             long endTimeMillis, in RemoteCallback callback);
-    void getHistoricalOpsFromDiskRaw(int uid, String packageName, in String[] ops,
+    void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops,
             long beginTimeMillis, long endTimeMillis, in RemoteCallback callback);
     void offsetHistory(long duration);
     void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 8dde44e..9ce7ed1 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -153,17 +153,7 @@
      in IVoiceActionCheckCallback callback);
 
     /**
-     * Sets the transcribed voice to the given string.
+     * Provide hints for showing UI.
      */
-    void setTranscription(IVoiceInteractionService service, String transcription);
-
-    /**
-     * Indicates that the transcription session is finished.
-     */
-    void clearTranscription(IVoiceInteractionService service, boolean immediate);
-
-    /**
-     * Sets the voice state indication based upon the given value.
-     */
-    void setVoiceState(IVoiceInteractionService service, int state);
+    void setUiHints(in IVoiceInteractionService service, in Bundle hints);
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
index 674ad5b..bc757e2 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -16,6 +16,8 @@
 
  package com.android.internal.app;
 
+ import android.os.Bundle;
+
  oneway interface IVoiceInteractionSessionListener {
     /**
      * Called when a voice session is shown.
@@ -28,18 +30,7 @@
     void onVoiceSessionHidden();
 
     /**
-     * Called when voice assistant transcription has been updated to the given string.
+     * Called when UI hints were received.
      */
-    void onTranscriptionUpdate(in String transcription);
-
-    /**
-     * Called when voice transcription is completed.
-     */
-    void onTranscriptionComplete(in boolean immediate);
-
-    /**
-     * Called when the voice assistant's state has changed. Values are from
-     * VoiceInteractionService's VOICE_STATE* constants.
-     */
-    void onVoiceStateChange(in int state);
+    void onSetUiHints(in Bundle args);
  }
\ No newline at end of file
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index 258d081..d9fd3b5 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -21,7 +21,7 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.os.Trace;
+import android.os.AsyncTask;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -53,11 +53,11 @@
     protected WallpaperColors mLockColors;
 
     public ColorExtractor(Context context) {
-        this(context, new Tonal(context));
+        this(context, new Tonal(context), true /* immediately */);
     }
 
     @VisibleForTesting
-    public ColorExtractor(Context context, ExtractionType extractionType) {
+    public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately) {
         mContext = context;
         mExtractionType = extractionType;
 
@@ -71,23 +71,48 @@
         }
 
         mOnColorsChangedListeners = new ArrayList<>();
-        GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
-        GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
 
         WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
         if (wallpaperManager == null) {
             Log.w(TAG, "Can't listen to color changes!");
         } else {
             wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
+            initExtractColors(wallpaperManager, immediately);
+        }
+    }
 
-            // Initialize all gradients with the current colors
-            Trace.beginSection("ColorExtractor#getWallpaperColors");
+    private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
+        if (immediately) {
             mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
             mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
-            Trace.endSection();
+            extractWallpaperColors();
+        } else {
+            new LoadWallpaperColors().executeOnExecutor(
+                    AsyncTask.THREAD_POOL_EXECUTOR, wallpaperManager);
         }
+    }
 
-        // Initialize all gradients with the current colors
+    private class LoadWallpaperColors extends AsyncTask<WallpaperManager, Void, Void> {
+        private WallpaperColors mSystemColors;
+        private WallpaperColors mLockColors;
+        @Override
+        protected Void doInBackground(WallpaperManager... params) {
+            mSystemColors = params[0].getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+            mLockColors = params[0].getWallpaperColors(WallpaperManager.FLAG_LOCK);
+            return null;
+        }
+        @Override
+        protected void onPostExecute(Void b) {
+            ColorExtractor.this.mSystemColors = mSystemColors;
+            ColorExtractor.this.mLockColors = mLockColors;
+            extractWallpaperColors();
+            triggerColorsChanged(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
+        }
+    }
+
+    private void extractWallpaperColors() {
+        GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
+        GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
         extractInto(mSystemColors,
                 systemColors[TYPE_NORMAL],
                 systemColors[TYPE_DARK],
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index eada142..22c832e 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -17,10 +17,13 @@
 package com.android.internal.os;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton background thread for each process.
  */
@@ -29,6 +32,7 @@
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static BackgroundThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private BackgroundThread() {
         super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -43,6 +47,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -59,4 +64,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (BackgroundThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 52e1748..4ff9948 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -187,6 +187,8 @@
     static final int MSG_REPORT_RESET_STATS = 4;
     static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
+    private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
+
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
@@ -252,6 +254,9 @@
     private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
     /** Last time that RPM stats were updated by updateRpmStatsLocked. */
     private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+
+    /** Container for Rail Energy Data stats. */
+    private final RailStats mTmpRailStats = new RailStats();
     /**
      * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
      * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -327,6 +332,15 @@
         public String getSubsystemLowPowerStats();
     }
 
+    /** interface to update rail information for power monitor */
+    public interface RailEnergyDataCallback {
+        /** Function to fill the map for the rail data stats
+         * Used for power monitoring feature
+         * @param railStats
+         */
+        void fillRailDataStats(RailStats railStats);
+    }
+
     public static abstract class UserInfoProvider {
         private int[] userIds;
         protected abstract @Nullable int[] getUserIds();
@@ -361,6 +375,8 @@
         }
     };
 
+    public final RailEnergyDataCallback mRailEnergyDataCallback;
+
     /**
      * This handler is running on {@link BackgroundThread}.
      */
@@ -593,7 +609,9 @@
         int UPDATE_RADIO = 0x04;
         int UPDATE_BT = 0x08;
         int UPDATE_RPM = 0x10; // 16
-        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
+        int UPDATE_RAIL = 0x20; // 32
+        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM
+                | UPDATE_RAIL;
 
         Future<?> scheduleSync(String reason, int flags);
         Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
@@ -1078,6 +1096,7 @@
         mBatteryStatsHistory = null;
         mHandler = null;
         mPlatformIdleStateCallback = null;
+        mRailEnergyDataCallback = null;
         mUserInfoProvider = null;
         mConstants = new Constants(mHandler);
         clearHistoryLocked();
@@ -3005,6 +3024,7 @@
         private final LongSamplingCounter mRxTimeMillis;
         private final LongSamplingCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
+        private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase);
@@ -3016,6 +3036,7 @@
                 mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
             }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase);
+            mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
         }
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
@@ -3033,6 +3054,7 @@
                 mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
             }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
+            mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
         }
 
         public void readSummaryFromParcel(Parcel in) {
@@ -3048,6 +3070,7 @@
                 counter.readSummaryFromParcelLocked(in);
             }
             mPowerDrainMaMs.readSummaryFromParcelLocked(in);
+            mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
         }
 
         @Override
@@ -3065,6 +3088,7 @@
                 counter.writeSummaryFromParcelLocked(dest);
             }
             mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
+            mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
         }
 
         @Override
@@ -3078,6 +3102,7 @@
                 counter.writeToParcel(dest);
             }
             mPowerDrainMaMs.writeToParcel(dest);
+            mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
         }
 
         public void reset(boolean detachIfReset) {
@@ -3089,6 +3114,7 @@
                 counter.reset(detachIfReset);
             }
             mPowerDrainMaMs.reset(detachIfReset);
+            mMonitoredRailChargeConsumedMaMs.reset(detachIfReset);
         }
 
         public void detach() {
@@ -3100,6 +3126,7 @@
                 counter.detach();
             }
             mPowerDrainMaMs.detach();
+            mMonitoredRailChargeConsumedMaMs.detach();
         }
 
         /**
@@ -3154,6 +3181,15 @@
         public LongSamplingCounter getPowerCounter() {
             return mPowerDrainMaMs;
         }
+
+        /**
+         * @return a LongSamplingCounter, measuring actual monitored rail energy consumed
+         * milli-ampere milli-seconds (mAmS).
+         */
+        @Override
+        public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
+            return mMonitoredRailChargeConsumedMaMs;
+        }
     }
 
     /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
@@ -3497,6 +3533,8 @@
             if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh);
             dest.writeInt(cur.batteryChargeUAh);
         }
+        dest.writeDouble(cur.modemRailChargeMah);
+        dest.writeDouble(cur.wifiRailChargeMah);
     }
 
     private int buildBatteryLevelInt(HistoryItem h) {
@@ -3747,6 +3785,8 @@
         if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) {
             cur.batteryChargeUAh = src.readInt();
         }
+        cur.modemRailChargeMah = src.readDouble();
+        cur.wifiRailChargeMah = src.readDouble();
     }
 
     @Override
@@ -10111,12 +10151,12 @@
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
-            UserInfoProvider userInfoProvider) {
-        this(new SystemClocks(), systemDir, handler, cb, userInfoProvider);
+            RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) {
+        this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider);
     }
 
     private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
-            PlatformIdleStateCallback cb,
+            PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb,
             UserInfoProvider userInfoProvider) {
         init(clocks);
 
@@ -10218,6 +10258,7 @@
         clearHistoryLocked();
         updateDailyDeadlineLocked();
         mPlatformIdleStateCallback = cb;
+        mRailEnergyDataCallback = railStatsCb;
         mUserInfoProvider = userInfoProvider;
     }
 
@@ -10238,6 +10279,7 @@
         mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
         readFromParcel(p);
         mPlatformIdleStateCallback = null;
+        mRailEnergyDataCallback = null;
     }
 
     public void setPowerProfileLocked(PowerProfile profile) {
@@ -10934,6 +10976,8 @@
             mWakeupReasonStats.clear();
         }
 
+        mTmpRailStats.reset();
+
         mLastHistoryStepDetails = null;
         mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
         mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -11321,6 +11365,16 @@
                     mWifiActivity.getPowerCounter().addCountLocked(
                             (long) (info.getControllerEnergyUsed() / opVolt));
                 }
+                // Converting uWs to mAms.
+                // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+                long monitoredRailChargeConsumedMaMs =
+                        (long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
+                mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+                        monitoredRailChargeConsumedMaMs);
+                mHistoryCur.wifiRailChargeMah +=
+                        (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+                addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                mTmpRailStats.resetWifiTotalEnergyUsed();
             }
         }
     }
@@ -11411,9 +11465,18 @@
 
                     // We store the power drain as mAms.
                     mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+                    // Converting uWs to mAms.
+                    // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+                    long monitoredRailChargeConsumedMaMs =
+                            (long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
+                    mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+                            monitoredRailChargeConsumedMaMs);
+                    mHistoryCur.modemRailChargeMah +=
+                            (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+                    addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                    mTmpRailStats.resetCellularTotalEnergyUsed();
                 }
             }
-
             final long elapsedRealtimeMs = mClocks.elapsedRealtime();
             long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
@@ -11812,6 +11875,16 @@
     }
 
     /**
+     * Read and record Rail Energy data.
+     */
+    public void updateRailStatsLocked() {
+        if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) {
+            return;
+        }
+        mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats);
+    }
+
+    /**
      * Read and distribute kernel wake lock use across apps.
      */
     public void updateKernelWakelocksLocked() {
@@ -12950,6 +13023,8 @@
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
         long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
         for (int i = 0; i < timeInRatMs.length; i++) {
            timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000;
@@ -12979,58 +13054,62 @@
         s.setTimeInRatMs(timeInRatMs);
         s.setTimeInRxSignalStrengthLevelMs(timeInRxSignalStrengthLevelMs);
         s.setTxTimeMs(txTimeMs);
+        s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
         return s;
     }
 
-     /*@hide */
-     public WifiBatteryStats getWifiBatteryStats() {
-         WifiBatteryStats s = new WifiBatteryStats();
-         final int which = STATS_SINCE_CHARGED;
-         final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
-         final ControllerActivityCounter counter = getWifiControllerActivity();
-         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
-         final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
-         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
-         final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
-         final long totalControllerActivityTimeMs
-             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
-         final long sleepTimeMs
-             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
-         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
-         long numAppScanRequest = 0;
-         for (int i = 0; i < mUidStats.size(); i++) {
-             numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
-         }
-         long[] timeInStateMs = new long[NUM_WIFI_STATES];
-         for (int i=0; i<NUM_WIFI_STATES; i++) {
+    /*@hide */
+    public WifiBatteryStats getWifiBatteryStats() {
+        WifiBatteryStats s = new WifiBatteryStats();
+        final int which = STATS_SINCE_CHARGED;
+        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+        final ControllerActivityCounter counter = getWifiControllerActivity();
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+        final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+        final long totalControllerActivityTimeMs
+                = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+        final long sleepTimeMs
+                = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+        final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
+        long numAppScanRequest = 0;
+        for (int i = 0; i < mUidStats.size(); i++) {
+            numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+        }
+        long[] timeInStateMs = new long[NUM_WIFI_STATES];
+        for (int i=0; i<NUM_WIFI_STATES; i++) {
             timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
-         }
-         long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
-         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
-             timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
-         }
-         long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
-         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
-             timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
-         }
-         s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
-         s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
-         s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
-         s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
-         s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
-         s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
-         s.setSleepTimeMs(sleepTimeMs);
-         s.setIdleTimeMs(idleTimeMs);
-         s.setRxTimeMs(rxTimeMs);
-         s.setTxTimeMs(txTimeMs);
-         s.setScanTimeMs(scanTimeMs);
-         s.setEnergyConsumedMaMs(energyConsumedMaMs);
-         s.setNumAppScanRequest(numAppScanRequest);
-         s.setTimeInStateMs(timeInStateMs);
-         s.setTimeInSupplicantStateMs(timeInSupplStateMs);
-         s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
-         return s;
-     }
+        }
+        long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+        }
+        long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+        }
+        s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+        s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+        s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+        s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+        s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+        s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+        s.setSleepTimeMs(sleepTimeMs);
+        s.setIdleTimeMs(idleTimeMs);
+        s.setRxTimeMs(rxTimeMs);
+        s.setTxTimeMs(txTimeMs);
+        s.setScanTimeMs(scanTimeMs);
+        s.setEnergyConsumedMaMs(energyConsumedMaMs);
+        s.setNumAppScanRequest(numAppScanRequest);
+        s.setTimeInStateMs(timeInStateMs);
+        s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+        s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+        s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
+        return s;
+    }
 
     /*@hide */
     public GpsBatteryStats getGpsBatteryStats() {
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index b1328e8..6bbfc2b 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -109,6 +109,12 @@
     private Predicate<Integer> mUidPredicate;
 
     /**
+     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+     * will not be reported
+     */
+    private int mMinimumTotalCpuUsageMillis;
+
+    /**
      * Where the proc filesystem is mounted
      */
     private final Path mProcPath;
@@ -142,10 +148,12 @@
     public KernelCpuThreadReader(
             int numBuckets,
             Predicate<Integer> uidPredicate,
+            int minimumTotalCpuUsageMillis,
             Path procPath,
             Path initialTimeInStatePath,
             Injector injector) throws IOException {
         mUidPredicate = uidPredicate;
+        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
         mProcPath = procPath;
         mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
         mInjector = injector;
@@ -158,11 +166,13 @@
      * @return the reader, null if an exception was thrown during creation
      */
     @Nullable
-    public static KernelCpuThreadReader create(int numBuckets, Predicate<Integer> uidPredicate) {
+    public static KernelCpuThreadReader create(
+            int numBuckets, Predicate<Integer> uidPredicate, int minimumTotalCpuUsageMillis) {
         try {
             return new KernelCpuThreadReader(
                     numBuckets,
                     uidPredicate,
+                    minimumTotalCpuUsageMillis,
                     DEFAULT_PROC_PATH,
                     DEFAULT_INITIAL_TIME_IN_STATE_PATH,
                     new Injector());
@@ -308,6 +318,18 @@
     }
 
     /**
+     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+     * will not be reported
+     */
+    void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
+        if (minimumTotalCpuUsageMillis < 0) {
+            Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
+            return;
+        }
+        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
+    }
+
+    /**
      * Get the CPU frequencies that correspond to the times reported in
      * {@link ThreadCpuUsage#usageTimesMillis}
      */
@@ -346,6 +368,15 @@
         }
         int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
 
+        // Check if the total CPU usage below the threshold
+        int totalCpuUsage = 0;
+        for (int i = 0; i < cpuUsages.length; i++) {
+            totalCpuUsage += cpuUsages[i];
+        }
+        if (totalCpuUsage < mMinimumTotalCpuUsageMillis) {
+            return null;
+        }
+
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index 77f6a17..718bcb4 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -59,6 +59,13 @@
     private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
     private static final String COLLECTED_UIDS_DEFAULT = "1000-1000";
 
+    /**
+     * Minimum total CPU usage to report
+     */
+    private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
+            "minimum_total_cpu_usage_millis";
+    private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 0;
+
     private final Context mContext;
 
     @Nullable
@@ -87,7 +94,8 @@
         mContext = context;
         mKernelCpuThreadReader = KernelCpuThreadReader.create(
                 NUM_BUCKETS_DEFAULT,
-                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT));
+                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
     }
 
     @Override
@@ -124,6 +132,9 @@
         mKernelCpuThreadReader.setNumBuckets(
                 parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
         mKernelCpuThreadReader.setUidPredicate(uidPredicate);
+        mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt(
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
     }
 
     /**
diff --git a/core/java/com/android/internal/os/RailStats.java b/core/java/com/android/internal/os/RailStats.java
new file mode 100644
index 0000000..ff00831
--- /dev/null
+++ b/core/java/com/android/internal/os/RailStats.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.util.Map;
+
+/** Rail Stats Power Monitoring Class */
+public final class RailStats {
+    private static final String TAG = "RailStats";
+
+    private static final String WIFI_SUBSYSTEM = "wifi";
+    private static final String CELLULAR_SUBSYSTEM = "cellular";
+
+    private Map<Long, RailInfoData> mRailInfoData = new ArrayMap<>();
+
+    private long mCellularTotalEnergyUseduWs = 0;
+    private long mWifiTotalEnergyUseduWs = 0;
+    private boolean mRailStatsAvailability = true;
+
+    /** Updates the rail data map of all power monitor rails being monitored
+     * Function is called from native side
+     * @param index
+     * @param railName
+     * @param subSystemName
+     * @param timestampSinceBootMs
+     * @param energyUsedSinceBootuWs
+     */
+    public void updateRailData(long index, String railName, String subSystemName,
+            long timestampSinceBootMs, long energyUsedSinceBootuWs) {
+        if (!(subSystemName.equals(WIFI_SUBSYSTEM) || subSystemName.equals(CELLULAR_SUBSYSTEM))) {
+            return;
+        }
+        RailInfoData node = mRailInfoData.get(index);
+        if (node == null) {
+            mRailInfoData.put(index, new RailInfoData(index, railName, subSystemName,
+                    timestampSinceBootMs, energyUsedSinceBootuWs));
+            if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+                mWifiTotalEnergyUseduWs += energyUsedSinceBootuWs;
+                return;
+            }
+            if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+                mCellularTotalEnergyUseduWs += energyUsedSinceBootuWs;
+            }
+            return;
+        }
+        long timeSinceLastLogMs = timestampSinceBootMs - node.timestampSinceBootMs;
+        long energyUsedSinceLastLoguWs = energyUsedSinceBootuWs - node.energyUsedSinceBootuWs;
+        if (timeSinceLastLogMs < 0 || energyUsedSinceLastLoguWs < 0) {
+            energyUsedSinceLastLoguWs = node.energyUsedSinceBootuWs;
+        }
+        node.timestampSinceBootMs = timestampSinceBootMs;
+        node.energyUsedSinceBootuWs = energyUsedSinceBootuWs;
+        if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+            mWifiTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+            return;
+        }
+        if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+            mCellularTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+        }
+    }
+
+    /** resets the cellular total energy used aspect.
+     */
+    public void resetCellularTotalEnergyUsed() {
+        mCellularTotalEnergyUseduWs = 0;
+    }
+
+    /** resets the wifi total energy used aspect.
+     */
+    public void resetWifiTotalEnergyUsed() {
+        mWifiTotalEnergyUseduWs = 0;
+    }
+
+    public long getCellularTotalEnergyUseduWs() {
+        return mCellularTotalEnergyUseduWs;
+    }
+
+    public long getWifiTotalEnergyUseduWs() {
+        return mWifiTotalEnergyUseduWs;
+    }
+
+    /** reset the total energy subsystems
+     *
+     */
+    public void reset() {
+        mCellularTotalEnergyUseduWs = 0;
+        mWifiTotalEnergyUseduWs = 0;
+    }
+
+    public RailStats getRailStats() {
+        return this;
+    }
+
+    public void setRailStatsAvailability(boolean railStatsAvailability) {
+        mRailStatsAvailability = railStatsAvailability;
+    }
+
+    public boolean isRailStatsAvailable() {
+        return mRailStatsAvailability;
+    }
+
+    /** Container class to contain rail data information */
+    public static class RailInfoData {
+        private static final String TAG = "RailInfoData";
+        public long index;
+        public String railName;
+        public String subSystemName;
+        public long timestampSinceBootMs;
+        public long energyUsedSinceBootuWs;
+
+        private RailInfoData(long index, String railName, String subSystemName,
+                long timestampSinceBootMs, long energyUsedSinceBoot) {
+            this.index = index;
+            this.railName = railName;
+            this.subSystemName = subSystemName;
+            this.timestampSinceBootMs = timestampSinceBootMs;
+            this.energyUsedSinceBootuWs = energyUsedSinceBoot;
+        }
+
+        /** print the rail data
+         *
+         */
+        public void printData() {
+            Slog.d(TAG, "Index = " + index);
+            Slog.d(TAG, "RailName = " + railName);
+            Slog.d(TAG, "SubSystemName = " + subSystemName);
+            Slog.d(TAG, "TimestampSinceBootMs = " + timestampSinceBootMs);
+            Slog.d(TAG, "EnergyUsedSinceBootuWs = " + energyUsedSinceBootuWs);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 40d7868..22884ac 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -29,6 +29,7 @@
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.provider.DeviceConfig;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
@@ -129,21 +130,6 @@
     public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8;
 
     /**
-     * If the blastula pool should be created and used to start applications.
-     *
-     * Setting this value to false will disable the creation, maintenance, and use of the blastula
-     * pool.  When the blastula pool is disabled the application lifecycle will be identical to
-     * previous versions of Android.
-     */
-    public static final boolean BLASTULA_POOL_ENABLED = false;
-
-    /**
-     * File descriptor used for communication between the signal handler and the ZygoteServer poll
-     * loop.
-     * */
-    protected static FileDescriptor sBlastulaPoolEventFD;
-
-    /**
      * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
      * in the abstract socket namespace. This socket name is what the new child zygote
      * should listen for connections on.
@@ -174,44 +160,40 @@
     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
 
     /**
-     * The maximum value that the sBlastulaPoolMax variable may take.  This value
-     * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
+     * The duration to wait before re-checking Zygote related system properties.
+     *
+     * Five minutes in milliseconds.
      */
-    static final int BLASTULA_POOL_MAX_LIMIT = 10;
-
-    /**
-     * The minimum value that the sBlastulaPoolMin variable may take.
-     */
-    static final int BLASTULA_POOL_MIN_LIMIT = 1;
-
-    /**
-     * The runtime-adjustable maximum Blastula pool size.
-     */
-    static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT;
-
-    /**
-     * The runtime-adjustable minimum Blastula pool size.
-     */
-    static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT;
-
-    /**
-     * The runtime-adjustable value used to determine when to re-fill the
-     * blastula pool.  The pool will be re-filled when
-     * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
-     */
-    // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
-    static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);
+    public static final long PROPERTY_CHECK_INTERVAL = 300000;
 
     /**
      * @hide for internal use only
      */
     public static final int SOCKET_BUFFER_SIZE = 256;
 
-    private static LocalServerSocket sBlastulaPoolSocket = null;
-
     /** a prototype instance for a future List.toArray() */
     protected static final int[][] INT_ARRAY_2D = new int[0][0];
 
+    /**
+     * @hide for internal use only.
+     */
+    public static final String PRIMARY_SOCKET_NAME = "zygote";
+
+    /**
+     * @hide for internal use only.
+     */
+    public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";
+
+    /**
+     * @hide for internal use only
+     */
+    public static final String BLASTULA_POOL_PRIMARY_SOCKET_NAME = "blastula_pool";
+
+    /**
+     * @hide for internal use only
+     */
+    public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary";
+
     private Zygote() {}
 
     /** Called for some security initialization before any fork. */
@@ -428,70 +410,47 @@
     protected static native void nativeGetSocketFDs(boolean isPrimary);
 
     /**
-     * Initialize the blastula pool and fill it with the desired number of
-     * processes.
+     * Returns the raw string value of a system property.
+     *
+     * Note that Device Config is not available without an application so SystemProperties is used
+     * instead.
+     *
+     * TODO (chriswailes): Cache the system property location in native code and then write a JNI
+     *                     function to fetch it.
      */
-    protected static Runnable initBlastulaPool() {
-        if (BLASTULA_POOL_ENABLED) {
-            sBlastulaPoolEventFD = getBlastulaPoolEventFD();
-
-            return fillBlastulaPool(null);
-        } else {
-            return null;
-        }
+    public static String getSystemProperty(String propertyName, String defaultValue) {
+        return SystemProperties.get(
+                String.join(".",
+                        "persist.device_config",
+                        DeviceConfig.RuntimeNative.NAMESPACE,
+                        propertyName),
+                defaultValue);
     }
 
     /**
-     * Checks to see if the current policy says that pool should be refilled, and spawns new
-     * blastulas if necessary.
+     * Returns the value of a system property converted to a boolean using specific logic.
      *
-     * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is
-     *       only called from contexts that are only valid if the pool is enabled.
+     * Note that Device Config is not available without an application so SystemProperties is used
+     * instead.
      *
-     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
-     * @return In the Zygote process this function will always return null; in blastula processes
-     *         this function will return a Runnable object representing the new application that is
-     *         passed up from blastulaMain.
+     * @see SystemProperties.getBoolean
+     *
+     * TODO (chriswailes): Cache the system property location in native code and then write a JNI
+     *                     function to fetch it.
      */
-    protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
-
-        int blastulaPoolCount = getBlastulaPoolCount();
-
-        int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount;
-
-        if (blastulaPoolCount < sBlastulaPoolMin
-                || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) {
-
-            // Disable some VM functionality and reset some system values
-            // before forking.
-            ZygoteHooks.preFork();
-            resetNicePriority();
-
-            while (blastulaPoolCount++ < sBlastulaPoolMax) {
-                Runnable caller = forkBlastula(sessionSocketRawFDs);
-
-                if (caller != null) {
-                    return caller;
-                }
-            }
-
-            // Re-enable runtime services for the Zygote.  Blastula services
-            // are re-enabled in specializeBlastula.
-            ZygoteHooks.postForkCommon();
-
-            Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
-        }
-
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-        return null;
+    public static boolean getSystemPropertyBoolean(String propertyName, Boolean defaultValue) {
+        return SystemProperties.getBoolean(
+                String.join(".",
+                        "persist.device_config",
+                        DeviceConfig.RuntimeNative.NAMESPACE,
+                        propertyName),
+                defaultValue);
     }
 
     /**
      * @return Number of blastulas currently in the pool
      */
-    private static int getBlastulaPoolCount() {
+    static int getBlastulaPoolCount() {
         return nativeGetBlastulaPoolCount();
     }
 
@@ -501,7 +460,7 @@
      * @return The event FD used for communication between the signal handler and the ZygoteServer
      *         poll loop
      */
-    private static FileDescriptor getBlastulaPoolEventFD() {
+    static FileDescriptor getBlastulaPoolEventFD() {
         FileDescriptor fd = new FileDescriptor();
         fd.setInt$(nativeGetBlastulaPoolEventFD());
 
@@ -518,7 +477,8 @@
      *         this function will return a Runnable object representing the new application that is
      *         passed up from blastulaMain.
      */
-    private static Runnable forkBlastula(int[] sessionSocketRawFDs) {
+    static Runnable forkBlastula(LocalServerSocket blastulaPoolSocket,
+                                 int[] sessionSocketRawFDs) {
         FileDescriptor[] pipeFDs = null;
 
         try {
@@ -532,7 +492,7 @@
 
         if (pid == 0) {
             IoUtils.closeQuietly(pipeFDs[0]);
-            return blastulaMain(pipeFDs[1]);
+            return blastulaMain(blastulaPoolSocket, pipeFDs[1]);
         } else {
             // The read-end of the pipe will be closed by the native code.
             // See removeBlastulaTableEntry();
@@ -553,7 +513,8 @@
      *                   of the ZygoteServer.
      * @return A runnable oject representing the new application.
      */
-    static Runnable blastulaMain(FileDescriptor writePipe) {
+    private static Runnable blastulaMain(LocalServerSocket blastulaPoolSocket,
+                                         FileDescriptor writePipe) {
         final int pid = Process.myPid();
 
         LocalSocket sessionSocket = null;
@@ -563,7 +524,7 @@
 
         while (true) {
             try {
-                sessionSocket = sBlastulaPoolSocket.accept();
+                sessionSocket = blastulaPoolSocket.accept();
 
                 BufferedReader blastulaReader =
                         new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
@@ -611,7 +572,7 @@
             System.exit(-1);
         } finally {
             IoUtils.closeQuietly(sessionSocket);
-            IoUtils.closeQuietly(sBlastulaPoolSocket);
+            IoUtils.closeQuietly(blastulaPoolSocket);
         }
 
         try {
@@ -660,7 +621,7 @@
      * exception if an invalid arugment is encountered.
      * @param args  The arguments to test
      */
-    static void validateBlastulaCommand(ZygoteArguments args) {
+    private static void validateBlastulaCommand(ZygoteArguments args) {
         if (args.mAbiListQuery) {
             throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list");
         } else if (args.mPidQuery) {
@@ -851,20 +812,6 @@
     }
 
     /**
-     * Creates a managed object representing the Blastula pool socket that has
-     * already been initialized and bound by init.
-     *
-     * TODO (chriswailes): Move the name selection logic into this function.
-     *
-     * @throws RuntimeException when open fails
-     */
-    static void createBlastulaSocket(String socketName) {
-        if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) {
-            sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName);
-        }
-    }
-
-    /**
      * Creates a managed LocalServerSocket object using a file descriptor
      * created by an init.rc script.  The init scripts that specify the
      * sockets name can be found in system/core/rootdir.  The socket is bound
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9cf7e27..8a878e2 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -338,13 +338,25 @@
 
         private final MetricsLogger mMetricsLogger = new MetricsLogger();
         private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger();
+        private int mHiddenApiAccessLogSampleRate = 0;
+
+        public static void setHiddenApiAccessLogSampleRate(int sampleRate) {
+            sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
+        }
 
         public static HiddenApiUsageLogger getInstance() {
             return HiddenApiUsageLogger.sInstance;
         }
 
-        public void hiddenApiUsed(String packageName, String signature,
+        public void hiddenApiUsed(int sampledValue, String packageName, String signature,
                 int accessMethod, boolean accessDenied) {
+            if (sampledValue < mHiddenApiAccessLogSampleRate) {
+                logUsage(packageName, signature, accessMethod, accessDenied);
+            }
+        }
+
+        private void logUsage(String packageName, String signature, int accessMethod,
+                                  boolean accessDenied) {
             int accessMethodMetric = HiddenApiUsageLogger.ACCESS_METHOD_NONE;
             switch(accessMethod) {
                 case HiddenApiUsageLogger.ACCESS_METHOD_NONE:
@@ -375,6 +387,7 @@
     private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
         try {
             ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
+            HiddenApiUsageLogger.setHiddenApiAccessLogSampleRate(samplingRate);
             ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
             mSocketOutStream.writeInt(0);
         } catch (IOException ioe) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index e132abd..7cddf75 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -755,7 +755,7 @@
     }
 
     public static void main(String argv[]) {
-        ZygoteServer zygoteServer = new ZygoteServer();
+        ZygoteServer zygoteServer = null;
 
         // Mark zygote start. This ensures that thread creation will throw
         // an error.
@@ -783,7 +783,7 @@
             RuntimeInit.enableDdms();
 
             boolean startSystemServer = false;
-            String socketName = "zygote";
+            String zygoteSocketName = "zygote";
             String abiList = null;
             boolean enableLazyPreload = false;
             for (int i = 1; i < argv.length; i++) {
@@ -794,26 +794,19 @@
                 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                     abiList = argv[i].substring(ABI_LIST_ARG.length());
                 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
-                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
+                    zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
                 } else {
                     throw new RuntimeException("Unknown command line argument: " + argv[i]);
                 }
             }
 
+            final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
+
             if (abiList == null) {
                 throw new RuntimeException("No ABI list supplied.");
             }
 
-            // TODO (chriswailes): Wrap these three calls in a helper function?
-            final String blastulaSocketName =
-                    socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)
-                            ? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME
-                            : ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME;
-
-            zygoteServer.createZygoteSocket(socketName);
-            Zygote.createBlastulaSocket(blastulaSocketName);
-
-            Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME));
+            Zygote.getSocketFDs(isPrimaryZygote);
 
             // In some configurations, we avoid preloading resources and classes eagerly.
             // In such cases, we will preload things prior to our first fork.
@@ -846,8 +839,10 @@
 
             ZygoteHooks.stopZygoteNoThreadCreation();
 
+            zygoteServer = new ZygoteServer(isPrimaryZygote);
+
             if (startSystemServer) {
-                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
+                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
 
                 // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                 // child (system_server) process.
@@ -857,23 +852,18 @@
                 }
             }
 
-            // If the return value is null then this is the zygote process
-            // returning to the normal control flow.  If it returns a Runnable
-            // object then this is a blastula that has finished specializing.
-            caller = Zygote.initBlastulaPool();
+            Log.i(TAG, "Accepting command socket connections");
 
-            if (caller == null) {
-                Log.i(TAG, "Accepting command socket connections");
-
-                // The select loop returns early in the child process after a fork and
-                // loops forever in the zygote.
-                caller = zygoteServer.runSelectLoop(abiList);
-            }
+            // The select loop returns early in the child process after a fork and
+            // loops forever in the zygote.
+            caller = zygoteServer.runSelectLoop(abiList);
         } catch (Throwable ex) {
             Log.e(TAG, "System zygote died with exception", ex);
             throw ex;
         } finally {
-            zygoteServer.closeServerSocket();
+            if (zygoteServer != null) {
+                zygoteServer.closeServerSocket();
+            }
         }
 
         // We're in the child process and have exited the select loop. Proceed to execute the
@@ -894,8 +884,8 @@
     }
 
     private static void waitForSecondaryZygote(String socketName) {
-        String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName)
-                ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME;
+        String otherZygoteName = Zygote.PRIMARY_SOCKET_NAME.equals(socketName)
+                ? Zygote.SECONDARY_SOCKET_NAME : Zygote.PRIMARY_SOCKET_NAME;
         ZygoteProcess.waitForConnectionToZygote(otherZygoteName);
     }
 
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a78c095..2c17540 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -20,12 +20,17 @@
 
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.provider.DeviceConfig;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructPollfd;
 import android.util.Log;
 import android.util.Slog;
 
+import dalvik.system.ZygoteHooks;
+
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.FileDescriptor;
@@ -38,7 +43,7 @@
  * Provides functions to wait for commands on a UNIX domain socket, and fork
  * off child processes that inherit the initial state of the VM.%
  *
- * Please see {@link ZygoteConnection.Arguments} for documentation on the
+ * Please see {@link ZygoteArguments} for documentation on the
  * client protocol.
  */
 class ZygoteServer {
@@ -46,11 +51,48 @@
     public static final String TAG = "ZygoteServer";
 
     /**
+     * The maximim value that will be accepted from the BLASTULA_POOL_SIZE_MAX device property.
+     * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
+     */
+    private static final int BLASTULA_POOL_SIZE_MAX_LIMIT = 100;
+
+    /**
+     * The minimum value that will be accepted from the BLASTULA_POOL_SIZE_MIN device property.
+     */
+    private static final int BLASTULA_POOL_SIZE_MIN_LIMIT = 1;
+
+    /** The default value used for the BLASTULA_POOL_SIZE_MAX device property */
+    private static final String BLASTULA_POOL_SIZE_MAX_DEFAULT = "10";
+
+    /** The default value used for the BLASTULA_POOL_SIZE_MIN device property */
+    private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1";
+
+    /**
+     * If the blastula pool should be created and used to start applications.
+     *
+     * Setting this value to false will disable the creation, maintenance, and use of the blastula
+     * pool.  When the blastula pool is disabled the application lifecycle will be identical to
+     * previous versions of Android.
+     */
+    private boolean mBlastulaPoolEnabled = false;
+
+    /**
      * Listening socket that accepts new server connections.
      */
     private LocalServerSocket mZygoteSocket;
 
     /**
+     * The name of the blastula socket to use if the blastula pool is enabled.
+     */
+    private LocalServerSocket mBlastulaPoolSocket;
+
+    /**
+     * File descriptor used for communication between the signal handler and the ZygoteServer poll
+     * loop.
+     * */
+    private FileDescriptor mBlastulaPoolEventFD;
+
+    /**
      * Whether or not mZygoteSocket's underlying FD should be closed directly.
      * If mZygoteSocket is created with an existing FD, closing the socket does
      * not close the FD and it must be closed explicitly. If the socket is created
@@ -64,25 +106,55 @@
      */
     private boolean mIsForkChild;
 
-    ZygoteServer() { }
+    /**
+     * The runtime-adjustable maximum Blastula pool size.
+     */
+    private int mBlastulaPoolSizeMax = 0;
 
-    void setForkChild() {
-        mIsForkChild = true;
+    /**
+     * The runtime-adjustable minimum Blastula pool size.
+     */
+    private int mBlastulaPoolSizeMin = 0;
+
+    /**
+     * The runtime-adjustable value used to determine when to re-fill the
+     * blastula pool.  The pool will be re-filled when
+     * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
+     */
+    private int mBlastulaPoolRefillThreshold = 0;
+
+    ZygoteServer() {
+        mBlastulaPoolEventFD = null;
+        mZygoteSocket = null;
+        mBlastulaPoolSocket = null;
     }
 
     /**
-     * Creates a managed object representing the Zygote socket that has already
-     * been initialized and bound by init.
+     * Initialize the Zygote server with the Zygote server socket, blastula pool server socket,
+     * and blastula pool event FD.
      *
-     * TODO (chriswailes): Move the name selection logic into this function.
-     *
-     * @throws RuntimeException when open fails
+     * @param isPrimaryZygote  If this is the primary Zygote or not.
      */
-    void createZygoteSocket(String socketName) {
-        if (mZygoteSocket == null) {
-            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(socketName);
-            mCloseSocketFd = true;
+    ZygoteServer(boolean isPrimaryZygote) {
+        mBlastulaPoolEventFD = Zygote.getBlastulaPoolEventFD();
+
+        if (isPrimaryZygote) {
+            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
+            mBlastulaPoolSocket =
+                    Zygote.createManagedSocketFromInitSocket(
+                            Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME);
+        } else {
+            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
+            mBlastulaPoolSocket =
+                    Zygote.createManagedSocketFromInitSocket(
+                            Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME);
         }
+
+        fetchBlastulaPoolPolicyProps();
+    }
+
+    void setForkChild() {
+        mIsForkChild = true;
     }
 
     /**
@@ -151,6 +223,102 @@
         return mZygoteSocket.getFileDescriptor();
     }
 
+    private void fetchBlastulaPoolPolicyProps() {
+        final String blastulaPoolSizeMaxPropString =
+                Zygote.getSystemProperty(
+                        DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
+                        BLASTULA_POOL_SIZE_MAX_DEFAULT);
+
+        if (!blastulaPoolSizeMaxPropString.isEmpty()) {
+            mBlastulaPoolSizeMax =
+                    Integer.min(
+                            Integer.parseInt(blastulaPoolSizeMaxPropString),
+                            BLASTULA_POOL_SIZE_MAX_LIMIT);
+        }
+
+        final String blastulaPoolSizeMinPropString =
+                Zygote.getSystemProperty(
+                        DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
+                        BLASTULA_POOL_SIZE_MIN_DEFAULT);
+
+        if (!blastulaPoolSizeMinPropString.isEmpty()) {
+            mBlastulaPoolSizeMin =
+                    Integer.max(
+                            Integer.parseInt(blastulaPoolSizeMinPropString),
+                            BLASTULA_POOL_SIZE_MIN_LIMIT);
+        }
+
+        final String blastulaPoolRefillThresholdPropString =
+                Zygote.getSystemProperty(
+                        DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
+                        Integer.toString(mBlastulaPoolSizeMax / 2));
+
+        if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
+            mBlastulaPoolRefillThreshold =
+                    Integer.min(
+                        Integer.parseInt(blastulaPoolRefillThresholdPropString),
+                        mBlastulaPoolSizeMax);
+        }
+
+    }
+
+    private long mLastPropCheckTimestamp = 0;
+
+    private void fetchBlastulaPoolPolicyPropsWithMinInterval() {
+        final long currentTimestamp = SystemClock.elapsedRealtime();
+
+        if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) {
+            fetchBlastulaPoolPolicyProps();
+            mLastPropCheckTimestamp = currentTimestamp;
+        }
+    }
+
+    /**
+     * Checks to see if the current policy says that pool should be refilled, and spawns new
+     * blastulas if necessary.
+     *
+     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
+     * @return In the Zygote process this function will always return null; in blastula processes
+     *         this function will return a Runnable object representing the new application that is
+     *         passed up from blastulaMain.
+     */
+    private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+        if (mBlastulaPoolEnabled) {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
+
+            int blastulaPoolCount = Zygote.getBlastulaPoolCount();
+            int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
+
+            if (blastulaPoolCount < mBlastulaPoolSizeMin
+                    || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
+
+                // Disable some VM functionality and reset some system values
+                // before forking.
+                ZygoteHooks.preFork();
+                Zygote.resetNicePriority();
+
+                while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
+                    Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs);
+
+                    if (caller != null) {
+                        return caller;
+                    }
+                }
+
+                // Re-enable runtime services for the Zygote.  Blastula services
+                // are re-enabled in specializeBlastula.
+                ZygoteHooks.postForkCommon();
+
+                Log.i("zygote",
+                        "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
+            }
+
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+
+        return null;
+    }
+
     /**
      * Runs the zygote process's select loop. Accepts new connections as
      * they happen, and reads commands from connections one spawn-request's
@@ -164,6 +332,8 @@
         peers.add(null);
 
         while (true) {
+            fetchBlastulaPoolPolicyPropsWithMinInterval();
+
             int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
 
             // Space for all of the socket FDs, the Blastula Pool Event FD, and
@@ -181,7 +351,7 @@
 
             final int blastulaPoolEventFDIndex = pollIndex;
             pollFDs[pollIndex] = new StructPollfd();
-            pollFDs[pollIndex].fd = Zygote.sBlastulaPoolEventFD;
+            pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
             pollFDs[pollIndex].events = (short) POLLIN;
             ++pollIndex;
 
@@ -316,7 +486,7 @@
                                 .mapToInt(fd -> fd.getInt$())
                                 .toArray();
 
-                    final Runnable command = Zygote.fillBlastulaPool(sessionSocketRawFDs);
+                    final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
 
                     if (command != null) {
                         return command;
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index a8ad810..c46f867 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -137,6 +137,7 @@
         mDismissStartTarget = mTargets.get(0);
         mDismissEndTarget = mTargets.get(mTargets.size() - 1);
         mMiddleTarget = mTargets.get(mTargets.size() / 2);
+        mMiddleTarget.isMiddleTarget = true;
     }
 
     /**
@@ -438,6 +439,8 @@
 
         public final int flag;
 
+        public boolean isMiddleTarget;
+
         /**
          * Multiplier used to calculate distance to snap position. The lower this value, the harder
          * it's to snap on this target
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 195ae52..274a513 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -16,20 +16,25 @@
 
 package com.android.internal.util;
 
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Bundle;
+import android.util.Size;
 
 import java.io.IOException;
-import java.io.InputStream;
 
 /**
  * Utility class for image analysis and processing.
@@ -166,21 +171,18 @@
     /**
      * @see https://developer.android.com/topic/performance/graphics/load-bitmap
      */
-    public static int calculateInSampleSize(BitmapFactory.Options options,
-            int reqWidth, int reqHeight) {
-        // Raw height and width of image
-        final int height = options.outHeight;
-        final int width = options.outWidth;
+    public static int calculateSampleSize(Size currentSize, Size requestedSize) {
         int inSampleSize = 1;
 
-        if (height > reqHeight || width > reqWidth) {
-            final int halfHeight = height / 2;
-            final int halfWidth = width / 2;
+        if (currentSize.getHeight() > requestedSize.getHeight()
+                || currentSize.getWidth() > requestedSize.getWidth()) {
+            final int halfHeight = currentSize.getHeight() / 2;
+            final int halfWidth = currentSize.getWidth() / 2;
 
             // Calculate the largest inSampleSize value that is a power of 2 and keeps both
             // height and width larger than the requested height and width.
-            while ((halfHeight / inSampleSize) >= reqHeight
-                    && (halfWidth / inSampleSize) >= reqWidth) {
+            while ((halfHeight / inSampleSize) >= requestedSize.getHeight()
+                    && (halfWidth / inSampleSize) >= requestedSize.getWidth()) {
                 inSampleSize *= 2;
             }
         }
@@ -190,27 +192,27 @@
 
     /**
      * Load a bitmap, and attempt to downscale to the required size, to save
-     * on memory.
+     * on memory. Updated to use newer and more compatible ImageDecoder.
      *
      * @see https://developer.android.com/topic/performance/graphics/load-bitmap
      */
-    public static Bitmap decodeSampledBitmapFromStream(ContentResolver resolver,
-            Uri uri, int reqWidth, int reqHeight) throws IOException {
+    public static Bitmap loadThumbnail(ContentResolver resolver, Uri uri, Size size)
+            throws IOException {
 
-        final BitmapFactory.Options options = new BitmapFactory.Options();
-        try (InputStream is = resolver.openInputStream(uri)) {
-            // First decode with inJustDecodeBounds=true to check dimensions
-            options.inJustDecodeBounds = true;
-            BitmapFactory.decodeStream(is, null, options);
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) {
+            final Bundle opts = new Bundle();
+            opts.putParcelable(ContentResolver.EXTRA_SIZE, Point.convert(size));
 
-            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
-        }
+            return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+                return client.openTypedAssetFile(uri, "image/*", opts, null);
+            }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
 
-        // need to do this twice as the InputStream is consumed in the first call,
-        // and not all InputStreams support marks
-        try (InputStream is = resolver.openInputStream(uri)) {
-            options.inJustDecodeBounds = false;
-            return BitmapFactory.decodeStream(is, null, options);
+                    final int sample = calculateSampleSize(info.getSize(), size);
+                    if (sample > 1) {
+                        decoder.setTargetSampleSize(sample);
+                    }
+                });
         }
     }
 }
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index 21558d3..e90a8d5 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -329,13 +329,13 @@
     }
 
     /**
-     * Maximize the window by moving it to the maximized workspace stack.
+     * Maximize or restore the window by moving it to the maximized or freeform workspace stack.
      **/
-    private void maximizeWindow() {
+    private void toggleFreeformWindowingMode() {
         Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
         if (callback != null) {
             try {
-                callback.exitFreeformMode();
+                callback.toggleFreeformWindowingMode();
             } catch (RemoteException ex) {
                 Log.e(TAG, "Cannot change task workspace.");
             }
@@ -395,7 +395,7 @@
     @Override
     public boolean onSingleTapUp(MotionEvent e) {
         if (mClickTarget == mMaximize) {
-            maximizeWindow();
+            toggleFreeformWindowingMode();
         } else if (mClickTarget == mClose) {
             mOwner.dispatchOnWindowDismissed(
                     true /*finishTask*/, false /*suppressWindowTransition*/);
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index c0ba181..c7c423b 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.server.backup;
 
-import android.app.AppGlobals;
+import android.annotation.NonNull;
 import android.app.backup.BlobBackupHelper;
-import android.content.pm.IPackageManager;
+import android.os.UserHandle;
+import android.permission.PermissionManagerInternal;
 import android.util.Slog;
 
+import com.android.server.LocalServices;
+
 public class PermissionBackupHelper extends BlobBackupHelper {
     private static final String TAG = "PermissionBackup";
     private static final boolean DEBUG = false;
@@ -31,24 +34,26 @@
     // key under which the permission-grant state blob is committed to backup
     private static final String KEY_PERMISSIONS = "permissions";
 
-    private final int mUserId;
+    private final @NonNull UserHandle mUser;
+
+    private final @NonNull PermissionManagerInternal mPermissionManager;
 
     public PermissionBackupHelper(int userId) {
         super(STATE_VERSION, KEY_PERMISSIONS);
 
-        mUserId = userId;
+        mUser = UserHandle.of(userId);
+        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
     }
 
     @Override
     protected byte[] getBackupPayload(String key) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling backup of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    return pm.getPermissionGrantBackup(mUserId);
+                    return mPermissionManager.backupRuntimePermissions(mUser);
 
                 default:
                     Slog.w(TAG, "Unexpected backup key " + key);
@@ -61,14 +66,13 @@
 
     @Override
     protected void applyRestoredPayload(String key, byte[] payload) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling restore of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    pm.restorePermissionGrants(payload, mUserId);
+                    mPermissionManager.restoreRuntimePermissions(payload, mUser);
                     break;
 
                 default:
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 345058b..30e9937 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -163,6 +163,7 @@
         "android_media_AudioSystem.cpp",
         "android_media_AudioTrack.cpp",
         "android_media_AudioAttributes.cpp",
+        "android_media_AudioProductStrategies.cpp",
         "android_media_DeviceCallback.cpp",
         "android_media_JetPlayer.cpp",
         "android_media_MediaMetricsJNI.cpp",
@@ -230,6 +231,7 @@
     ],
 
     static_libs: [
+        "libasync_safe",
         "libgif",
         "libseccomp_policy",
         "libgrallocusage",
@@ -287,8 +289,8 @@
         "libsoundtrigger",
         "libminikin",
         "libprocessgroup",
-        "libnativebridge",
-        "libnativeloader",
+        "libnativebridge_lazy",
+        "libnativeloader_lazy",
         "libmemunreachable",
         "libhidlbase",
         "libhidltransport",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d69d416..019ade9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -109,6 +109,7 @@
 extern int register_android_media_AudioSystem(JNIEnv *env);
 extern int register_android_media_AudioTrack(JNIEnv *env);
 extern int register_android_media_AudioAttributes(JNIEnv *env);
+extern int register_android_media_AudioProductStrategies(JNIEnv *env);
 extern int register_android_media_MicrophoneInfo(JNIEnv *env);
 extern int register_android_media_JetPlayer(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
@@ -232,8 +233,15 @@
 
 // Namespace for Android Runtime flags applied during boot time.
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
-// Feature flag name for Garbage Collector type.
-static const char* GCTYPE = "gctype";
+// Feature flag name to enable/disable generational garbage collection in ART's
+// Concurrent Copying (CC) garbage collector.
+static const char* ENABLE_GENERATIONAL_CC = "enable_generational_cc";
+// Runtime option enabling generational garbage collection in ART's Concurrent
+// Copying (CC) garbage collector.
+static const char* kGenerationalCCRuntimeOption = "-Xgc:generational_cc";
+// Runtime option disabling generational garbage collection in ART's Concurrent
+// Copying (CC) garbage collector.
+static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -667,6 +675,11 @@
     char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];
     std::string fingerprintBuf;
     char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
+    char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
+
+    if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
+        ALOGI("Boot image: '%s'\n", bootImageBuf);
+    }
 
     bool checkJni = false;
     property_get("dalvik.vm.checkjni", propBuf, "");
@@ -780,17 +793,21 @@
       addOption("-XX:LowMemoryMode");
     }
 
-    std::string gc_type_override =
-            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
-                                                                 GCTYPE,
-                                                                 /*default_value=*/ "");
-    std::string gc_type_override_temp;
-    if (gc_type_override.empty()) {
-        parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");
-    } else {
-        // Copy the string so it doesn't go out of scope since addOption does not make a copy.
-        gc_type_override_temp = "-Xgc:" + gc_type_override;
-        addOption(gc_type_override_temp.c_str());
+    /*
+     * Garbage-collection related options.
+     */
+    parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");
+
+    // If it set, honor the "enable_generational_cc" device configuration;
+    // otherwise, let the runtime use its default behavior.
+    std::string enable_generational_cc =
+        server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+                                                             ENABLE_GENERATIONAL_CC,
+                                                             /*default_value=*/ "");
+    if (enable_generational_cc == "true") {
+        addOption(kGenerationalCCRuntimeOption);
+    } else if (enable_generational_cc == "false") {
+        addOption(kNoGenerationalCCRuntimeOption);
     }
 
     parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC=");
@@ -1490,6 +1507,7 @@
     REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioTrack),
     REG_JNI(register_android_media_AudioAttributes),
+    REG_JNI(register_android_media_AudioProductStrategies),
     REG_JNI(register_android_media_JetPlayer),
     REG_JNI(register_android_media_MicrophoneInfo),
     REG_JNI(register_android_media_RemoteDisplay),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 774c224..7ff15f2 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -6,4 +6,4 @@
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
 # Zygote
-per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
+per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e817217..8f00759 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -355,9 +355,16 @@
         colorType = kN32_SkColorType;
     }
 
+    sk_sp<SkColorSpace> colorSpace;
+    if (colorType == kAlpha_8_SkColorType) {
+        colorSpace = nullptr;
+    } else {
+        colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
+    }
+
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
-                GraphicsJNI::getNativeColorSpace(colorSpacePtr)));
+                colorSpace));
 
     sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
     if (!nativeBitmap) {
@@ -385,15 +392,17 @@
         case kRGB_565_SkColorType:
             dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
             break;
-        case kRGBA_F16_SkColorType:
-            // The caller does not have an opportunity to pass a dst color space.  Assume that
-            // they want linear sRGB.
-            dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
+        case kAlpha_8_SkColorType:
+            dstInfo = dstInfo.makeColorSpace(nullptr);
             break;
         default:
             break;
     }
 
+    if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) {
+        dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB());
+    }
+
     if (!dst->setInfo(dstInfo)) {
         return false;
     }
@@ -608,14 +617,6 @@
     return static_cast<jint>(bitmap->getGenerationID());
 }
 
-static jboolean Bitmap_isConfigF16(JNIEnv* env, jobject, jlong bitmapHandle) {
-    LocalScopedBitmap bitmap(bitmapHandle);
-    if (bitmap->info().colorType() == kRGBA_F16_SkColorType) {
-        return JNI_TRUE;
-    }
-    return JNI_FALSE;
-}
-
 static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
     if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
@@ -684,9 +685,7 @@
     const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
     const uint32_t    colorSpaceSize = p->readUint32();
     sk_sp<SkColorSpace> colorSpace;
-    if (kRGBA_F16_SkColorType == colorType) {
-        colorSpace = SkColorSpace::MakeSRGBLinear();
-    } else if (colorSpaceSize > 0) {
+    if (colorSpaceSize > 0) {
         if (colorSpaceSize > kMaxColorSpaceSerializedBytes) {
             ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: "
                     "%d bytes\n", colorSpaceSize);
@@ -811,7 +810,7 @@
     p->writeInt32(bitmap.colorType());
     p->writeInt32(bitmap.alphaType());
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) {
+    if (colorSpace != nullptr) {
         sk_sp<SkData> data = colorSpace->serialize();
         size_t size = data->size();
         p->writeUint32(size);
@@ -924,44 +923,14 @@
     return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle,
-        jfloatArray xyzArray, jfloatArray paramsArray) {
-
+static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmapHolder(bitmapHandle);
-    if (!bitmapHolder.valid()) return JNI_FALSE;
+    if (!bitmapHolder.valid()) return nullptr;
 
     SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
-    if (colorSpace == nullptr) return JNI_FALSE;
+    if (colorSpace == nullptr) return nullptr;
 
-    skcms_Matrix3x3 xyzMatrix;
-    if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE;
-
-    jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL);
-    xyz[0] = xyzMatrix.vals[0][0];
-    xyz[1] = xyzMatrix.vals[1][0];
-    xyz[2] = xyzMatrix.vals[2][0];
-    xyz[3] = xyzMatrix.vals[0][1];
-    xyz[4] = xyzMatrix.vals[1][1];
-    xyz[5] = xyzMatrix.vals[2][1];
-    xyz[6] = xyzMatrix.vals[0][2];
-    xyz[7] = xyzMatrix.vals[1][2];
-    xyz[8] = xyzMatrix.vals[2][2];
-    env->ReleaseFloatArrayElements(xyzArray, xyz, 0);
-
-    skcms_TransferFunction transferParams;
-    if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE;
-
-    jfloat* params = env->GetFloatArrayElements(paramsArray, NULL);
-    params[0] = transferParams.a;
-    params[1] = transferParams.b;
-    params[2] = transferParams.c;
-    params[3] = transferParams.d;
-    params[4] = transferParams.e;
-    params[5] = transferParams.f;
-    params[6] = transferParams.g;
-    env->ReleaseFloatArrayElements(paramsArray, params, 0);
-
-    return JNI_TRUE;
+    return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType());
 }
 
 static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) {
@@ -1174,13 +1143,6 @@
     return createJavaGraphicBuffer(env, buffer);
 }
 
-static void Bitmap_copyColorSpace(JNIEnv* env, jobject, jlong srcBitmapPtr, jlong dstBitmapPtr) {
-    LocalScopedBitmap srcBitmapHandle(srcBitmapPtr);
-    LocalScopedBitmap dstBitmapHandle(dstBitmapPtr);
-
-    dstBitmapHandle->bitmap().setColorSpace(srcBitmapHandle->bitmap().info().refColorSpace());
-}
-
 static jboolean Bitmap_isImmutable(jlong bitmapHandle) {
     LocalScopedBitmap bitmapHolder(bitmapHandle);
     if (!bitmapHolder.valid()) return JNI_FALSE;
@@ -1215,7 +1177,6 @@
     {   "nativeErase",              "(JJJ)V", (void*)Bitmap_eraseLong },
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
-    {   "nativeIsConfigF16",        "(J)Z", (void*)Bitmap_isConfigF16 },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
     {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
     {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
@@ -1248,12 +1209,10 @@
         (void*) Bitmap_wrapHardwareBufferBitmap },
     {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
         (void*) Bitmap_createGraphicBufferHandle },
-    {   "nativeGetColorSpace",      "(J[F[F)Z", (void*)Bitmap_getColorSpace },
+    {   "nativeComputeColorSpace",  "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
     {   "nativeSetColorSpace",      "(JJ)V", (void*)Bitmap_setColorSpace },
     {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
     {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
-    {   "nativeCopyColorSpace",     "(JJ)V",
-        (void*)Bitmap_copyColorSpace },
     {   "nativeSetImmutable",       "(J)V", (void*)Bitmap_setImmutable},
 
     // ------------ @CriticalNative ----------------
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 70e6604..4ba4540 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -307,7 +307,7 @@
         env->SetObjectField(options, gOptions_outConfigFieldID, config);
 
         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
-                GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
 
         if (onlyDecodeSize) {
             return nullptr;
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index d65f324..9c07e2d 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -215,7 +215,7 @@
         env->SetObjectField(options, gOptions_outConfigFieldID, config);
 
         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
-                GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
     }
 
     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6570992..2987c5e 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -187,6 +187,8 @@
 
 static jclass gColorSpace_Named_class;
 static jfieldID gColorSpace_Named_sRGBFieldID;
+static jfieldID gColorSpace_Named_ExtendedSRGBFieldID;
+static jfieldID gColorSpace_Named_LinearSRGBFieldID;
 static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
 
 static jclass gTransferParameters_class;
@@ -412,67 +414,78 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
         SkColorType decodeColorType) {
-    jobject colorSpace = nullptr;
-
-    // No need to match, we know what the output color space will be
-    if (decodeColorType == kRGBA_F16_SkColorType) {
-        jobject linearExtendedSRGB = env->GetStaticObjectField(
-                gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID);
-        colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                gColorSpace_getMethodID, linearExtendedSRGB);
-    } else {
-        // Same here, no need to match
-        if (decodeColorSpace->isSRGB()) {
-            jobject sRGB = env->GetStaticObjectField(
-                    gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID);
-            colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                    gColorSpace_getMethodID, sRGB);
-        } else if (decodeColorSpace.get() != nullptr) {
-            // Try to match against known RGB color spaces using the CIE XYZ D50
-            // conversion matrix and numerical transfer function parameters
-            skcms_Matrix3x3 xyzMatrix;
-            LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
-
-            skcms_TransferFunction transferParams;
-            // We can only handle numerical transfer functions at the moment
-            LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
-
-            jobject params = env->NewObject(gTransferParameters_class,
-                    gTransferParameters_constructorMethodID,
-                    transferParams.a, transferParams.b, transferParams.c,
-                    transferParams.d, transferParams.e, transferParams.f,
-                    transferParams.g);
-
-            jfloatArray xyzArray = env->NewFloatArray(9);
-            jfloat xyz[9] = {
-                    xyzMatrix.vals[0][0],
-                    xyzMatrix.vals[1][0],
-                    xyzMatrix.vals[2][0],
-                    xyzMatrix.vals[0][1],
-                    xyzMatrix.vals[1][1],
-                    xyzMatrix.vals[2][1],
-                    xyzMatrix.vals[0][2],
-                    xyzMatrix.vals[1][2],
-                    xyzMatrix.vals[2][2]
-            };
-            env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
-
-            colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                    gColorSpace_matchMethodID, xyzArray, params);
-
-            if (colorSpace == nullptr) {
-                // We couldn't find an exact match, let's create a new color space
-                // instance with the 3x3 conversion matrix and transfer function
-                colorSpace = env->NewObject(gColorSpaceRGB_class,
-                        gColorSpaceRGB_constructorMethodID,
-                        env->NewStringUTF("Unknown"), xyzArray, params);
-            }
-
-            env->DeleteLocalRef(xyzArray);
-        }
+    if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) {
+        return nullptr;
     }
+
+    // Special checks for the common sRGB cases and their extended variants.
+    jobject namedCS = nullptr;
+    sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear();
+    if (decodeColorType == kRGBA_F16_SkColorType) {
+        // An F16 Bitmap will always report that it is EXTENDED if
+        // it matches a ColorSpace that has an EXTENDED variant.
+        if (decodeColorSpace->isSRGB()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_ExtendedSRGBFieldID);
+        } else if (decodeColorSpace == srgbLinear.get()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_LinearExtendedSRGBFieldID);
+        }
+    } else if (decodeColorSpace->isSRGB()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_sRGBFieldID);
+    } else if (decodeColorSpace == srgbLinear.get()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_LinearSRGBFieldID);
+    }
+
+    if (namedCS) {
+        return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS);
+    }
+
+    // Try to match against known RGB color spaces using the CIE XYZ D50
+    // conversion matrix and numerical transfer function parameters
+    skcms_Matrix3x3 xyzMatrix;
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
+
+    skcms_TransferFunction transferParams;
+    // We can only handle numerical transfer functions at the moment
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
+
+    jobject params = env->NewObject(gTransferParameters_class,
+            gTransferParameters_constructorMethodID,
+            transferParams.a, transferParams.b, transferParams.c,
+            transferParams.d, transferParams.e, transferParams.f,
+            transferParams.g);
+
+    jfloatArray xyzArray = env->NewFloatArray(9);
+    jfloat xyz[9] = {
+            xyzMatrix.vals[0][0],
+            xyzMatrix.vals[1][0],
+            xyzMatrix.vals[2][0],
+            xyzMatrix.vals[0][1],
+            xyzMatrix.vals[1][1],
+            xyzMatrix.vals[2][1],
+            xyzMatrix.vals[0][2],
+            xyzMatrix.vals[1][2],
+            xyzMatrix.vals[2][2]
+    };
+    env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
+
+    jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+            gColorSpace_matchMethodID, xyzArray, params);
+
+    if (colorSpace == nullptr) {
+        // We couldn't find an exact match, let's create a new color space
+        // instance with the 3x3 conversion matrix and transfer function
+        colorSpace = env->NewObject(gColorSpaceRGB_class,
+                gColorSpaceRGB_constructorMethodID,
+                env->NewStringUTF("Unknown"), xyzArray, params);
+    }
+
+    env->DeleteLocalRef(xyzArray);
     return colorSpace;
 }
 
@@ -658,6 +671,10 @@
             FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
     gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env,
             gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;");
     gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
             gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index dc0d022..f80651c 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -109,7 +109,13 @@
      */
     static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
 
-    static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+    /**
+     * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace
+     * and decodeColorType.
+     *
+     * This may create a new object if none of the Named ColorSpaces match.
+     */
+    static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
             SkColorType decodeColorType);
 
     /**
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 2d83ac3..9efcace 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -506,9 +506,9 @@
 
 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());
+    auto colorType = codec->computeOutputColorType(kN32_SkColorType);
     sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
-    return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
+    return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType);
 }
 
 static const JNINativeMethod gImageDecoderMethods[] = {
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
new file mode 100644
index 0000000..a18e80a
--- /dev/null
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioProductStrategies-JNI"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+
+#include <utils/Log.h>
+#include <vector>
+
+#include <media/AudioSystem.h>
+#include <media/AudioPolicy.h>
+
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "android_media_AudioAttributes.h"
+#include "android_media_AudioErrors.h"
+
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/audiopolicy/AudioProductStrategies";
+static const char* const kAudioProductStrategyClassPathName =
+        "android/media/audiopolicy/AudioProductStrategy";
+
+static const char* const kAudioAttributesGroupsClassPathName =
+        "android/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup";
+
+static jclass gAudioProductStrategyClass;
+static jmethodID gAudioProductStrategyCstor;
+static struct {
+    jfieldID    mAudioAttributesGroups;
+    jfieldID    mName;
+    jfieldID    mId;
+} gAudioProductStrategyFields;
+
+static jclass gAudioAttributesGroupClass;
+static jmethodID gAudioAttributesGroupCstor;
+static struct {
+    jfieldID    mGroupId;
+    jfieldID    mLegacyStreamType;
+    jfieldID    mAudioAttributes;
+} gAudioAttributesGroupsFields;
+
+static jclass gArrayListClass;
+static struct {
+    jmethodID    add;
+    jmethodID    toArray;
+} gArrayListMethods;
+
+
+static jint convertAudioProductStrategiesFromNative(
+        JNIEnv *env, jobject *jAudioStrategy, const AudioProductStrategy &strategy)
+{
+    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+    jobjectArray jAudioAttributesGroups = NULL;
+    jobjectArray jAudioAttributes = NULL;
+    jobject jAudioAttribute = NULL;
+    jstring jName = NULL;
+    jint jStrategyId = NULL;
+    jint numAttributesGroups;
+    size_t indexGroup = 0;
+
+    jName = env->NewStringUTF(strategy.getName().c_str());
+    jStrategyId = static_cast<jint>(strategy.getId());
+
+    // Audio Attributes Group array
+    std::map<int, std::vector<AudioAttributes> > groups;
+    for (const auto &attr : strategy.getAudioAttributes()) {
+        int attrGroupId = attr.getGroupId();
+        groups[attrGroupId].push_back(attr);
+    }
+    numAttributesGroups = groups.size();
+
+    jAudioAttributesGroups = env->NewObjectArray(numAttributesGroups, gAudioAttributesGroupClass, NULL);
+
+    for (const auto &iter : groups) {
+        std::vector<AudioAttributes> audioAttributesGroups = iter.second;
+        jint numAttributes = audioAttributesGroups.size();
+        jint jGroupId = iter.first;
+        jint jLegacyStreamType = audioAttributesGroups.front().getStreamType();
+
+        jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes);
+        if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) {
+            auto attributes = audioAttributesGroups[j].getAttributes();
+
+            jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes);
+            if (jStatus != AUDIO_JAVA_SUCCESS) {
+                goto exit;
+            }
+            env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute);
+        }
+        jobject jAudioAttributesGroup = env->NewObject(gAudioAttributesGroupClass,
+                                                       gAudioAttributesGroupCstor,
+                                                       jGroupId,
+                                                       jLegacyStreamType,
+                                                       jAudioAttributes);
+        env->SetObjectArrayElement(jAudioAttributesGroups, indexGroup++, jAudioAttributesGroup);
+
+        if (jAudioAttributes != NULL) {
+            env->DeleteLocalRef(jAudioAttributes);
+            jAudioAttributes = NULL;
+        }
+        if (jAudioAttribute != NULL) {
+            env->DeleteLocalRef(jAudioAttribute);
+            jAudioAttribute = NULL;
+        }
+        if (jAudioAttributesGroup != NULL) {
+            env->DeleteLocalRef(jAudioAttributesGroup);
+            jAudioAttributesGroup = NULL;
+        }
+    }
+    *jAudioStrategy = env->NewObject(gAudioProductStrategyClass, gAudioProductStrategyCstor,
+                                     jName,
+                                     jStrategyId,
+                                     jAudioAttributesGroups);
+exit:
+    if (jAudioAttributes != NULL) {
+        env->DeleteLocalRef(jAudioAttributes);
+    }
+    if (jAudioAttribute != NULL) {
+        env->DeleteLocalRef(jAudioAttribute);
+        jAudioAttribute = NULL;
+    }
+    if (jAudioAttributesGroups != NULL) {
+        env->DeleteLocalRef(jAudioAttributesGroups);
+    }
+    if (jName != NULL) {
+        env->DeleteLocalRef(jName);
+    }
+    return jStatus;
+}
+
+static jint
+android_media_AudioSystem_listAudioProductStrategies(JNIEnv *env, jobject clazz,
+                                                     jobject jStrategies)
+{
+    if (env == NULL) {
+        return AUDIO_JAVA_DEAD_OBJECT;
+    }
+    if (jStrategies == NULL) {
+        ALOGE("listAudioProductStrategies NULL AudioProductStrategies");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jStrategies, gArrayListClass)) {
+        ALOGE("listAudioProductStrategies not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    status_t status;
+    AudioProductStrategyVector strategies;
+    jint jStatus;
+    jobject jStrategy = NULL;
+
+    status = AudioSystem::listAudioProductStrategies(strategies);
+    if (status != NO_ERROR) {
+        ALOGE("AudioSystem::listAudioProductStrategies error %d", status);
+        return nativeToJavaStatus(status);
+    }
+    for (const auto &strategy : strategies) {
+        jStatus = convertAudioProductStrategiesFromNative(env, &jStrategy, strategy);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        env->CallBooleanMethod(jStrategies, gArrayListMethods.add, jStrategy);
+    }
+exit:
+    if (jStrategy != NULL) {
+        env->DeleteLocalRef(jStrategy);
+    }
+    return jStatus;
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+    {"native_list_audio_product_strategies", "(Ljava/util/ArrayList;)I",
+                        (void *)android_media_AudioSystem_listAudioProductStrategies},
+};
+
+int register_android_media_AudioProductStrategies(JNIEnv *env)
+{
+    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+    gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass,
+                                                 "toArray", "()[Ljava/lang/Object;");
+
+    jclass audioProductStrategyClass = FindClassOrDie(env, kAudioProductStrategyClassPathName);
+    gAudioProductStrategyClass = MakeGlobalRefOrDie(env, audioProductStrategyClass);
+    gAudioProductStrategyCstor = GetMethodIDOrDie(
+                env, audioProductStrategyClass, "<init>",
+                "(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V");
+    gAudioProductStrategyFields.mAudioAttributesGroups = GetFieldIDOrDie(
+                env, audioProductStrategyClass, "mAudioAttributesGroups",
+                "[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;");
+    gAudioProductStrategyFields.mName = GetFieldIDOrDie(
+                env, audioProductStrategyClass, "mName", "Ljava/lang/String;");
+    gAudioProductStrategyFields.mId = GetFieldIDOrDie(
+                env, audioProductStrategyClass, "mId", "I");
+
+    jclass audioAttributesGroupClass = FindClassOrDie(env, kAudioAttributesGroupsClassPathName);
+    gAudioAttributesGroupClass = MakeGlobalRefOrDie(env, audioAttributesGroupClass);
+    gAudioAttributesGroupCstor = GetMethodIDOrDie(env, audioAttributesGroupClass, "<init>",
+                                                  "(II[Landroid/media/AudioAttributes;)V");
+    gAudioAttributesGroupsFields.mGroupId = GetFieldIDOrDie(
+                env, audioAttributesGroupClass, "mGroupId", "I");
+    gAudioAttributesGroupsFields.mLegacyStreamType = GetFieldIDOrDie(
+                env, audioAttributesGroupClass, "mLegacyStreamType", "I");
+    gAudioAttributesGroupsFields.mAudioAttributes = GetFieldIDOrDie(
+                env, audioAttributesGroupClass, "mAudioAttributes",
+                "[Landroid/media/AudioAttributes;");
+
+    env->DeleteLocalRef(audioProductStrategyClass);
+    env->DeleteLocalRef(audioAttributesGroupClass);
+
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a2ed7d3..bd998999 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1918,7 +1918,6 @@
     for (jint i = 0; i < nb; i++) {
         deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
     }
-    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     // check each address is a string and add device type/address to list for device affinity
     Vector<AudioDeviceTypeAddr> deviceVector;
@@ -1932,6 +1931,7 @@
         AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
         deviceVector.add(dev);
     }
+    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
     return (jint) nativeToJavaStatus(status);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index e9035ed..195fe58 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -737,6 +737,25 @@
     return env->NewStringUTF(s.c_str());
 }
 
+static jlong android_os_Debug_getFreeZramKb(JNIEnv* env, jobject clazz) {
+
+    jlong zramFreeKb = 0;
+
+    std::string status_path = android::base::StringPrintf("/proc/meminfo");
+    UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
+
+    char line[256];
+    while (file != nullptr && fgets(line, sizeof(line), file.get())) {
+        jlong v;
+        if (sscanf(line, "SwapFree: %" SCNd64 " kB", &v) == 1) {
+            zramFreeKb = v;
+            break;
+        }
+    }
+
+    return zramFreeKb;
+}
+
 /*
  * JNI registration.
  */
@@ -778,6 +797,8 @@
             (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
     { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
             (void*)android_os_Debug_getUnreachableMemory },
+    { "getZramFreeKb", "()J",
+            (void*)android_os_Debug_getFreeZramKb },
 };
 
 int register_android_os_Debug(JNIEnv *env)
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index e2e66ce..0ccc327 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -34,13 +34,16 @@
 
 void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
                         jstring driverVersionName, jlong driverVersionCode,
-                        jstring appPackageName) {
+                        jstring driverBuildDate, jstring appPackageName) {
     ScopedUtfChars driverPackageNameChars(env, driverPackageName);
     ScopedUtfChars driverVersionNameChars(env, driverVersionName);
+    ScopedUtfChars driverBuildDateChars(env, driverBuildDate);
     ScopedUtfChars appPackageNameChars(env, appPackageName);
     android::GraphicsEnv::getInstance().setGpuStats(driverPackageNameChars.c_str(),
                                                     driverVersionNameChars.c_str(),
-                                                    driverVersionCode, appPackageNameChars.c_str());
+                                                    driverVersionCode,
+                                                    driverBuildDateChars.c_str(),
+                                                    appPackageNameChars.c_str());
 }
 
 void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn,
@@ -55,6 +58,11 @@
             devOptInChars.c_str(), rulesFd_native, rulesOffset, rulesLength);
 }
 
+bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
+    ScopedUtfChars appNameChars(env, appName);
+    return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str());
+}
+
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
     android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader(
         env, classLoader);
@@ -79,8 +87,9 @@
 const JNINativeMethod g_methods[] = {
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
-    { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) },
+    { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) },
     { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
+    { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
     { "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) },
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8c73630..ecc2dd0 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -33,7 +33,6 @@
 
 #include <private/EGL/cache.h>
 
-#include <utils/Looper.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
@@ -144,52 +143,22 @@
     uint32_t mRequestId;
 };
 
-class RenderingException : public MessageHandler {
+class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
 public:
-    RenderingException(JavaVM* vm, const std::string& message)
-            : mVm(vm)
-            , mMessage(message) {
-    }
-
-    virtual void handleMessage(const Message&) {
-        throwException(mVm, mMessage);
-    }
-
-    static void throwException(JavaVM* vm, const std::string& message) {
-        JNIEnv* env = getenv(vm);
-        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
-    }
-
-private:
-    JavaVM* mVm;
-    std::string mMessage;
-};
-
-class FrameCompleteWrapper : public MessageHandler {
-public:
-    FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
-        mLooper = Looper::getForThread();
-        LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!");
+    explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
         env->GetJavaVM(&mVm);
         mObject = env->NewGlobalRef(jobject);
         LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
     }
 
-    virtual ~FrameCompleteWrapper() {
+    ~FrameCompleteWrapper() {
         releaseObject();
     }
 
-    void postFrameComplete(int64_t frameNr) {
+    void onFrameComplete(int64_t frameNr) {
         if (mObject) {
-            mFrameNr = frameNr;
-            mLooper->sendMessage(this, 0);
-        }
-    }
-
-    virtual void handleMessage(const Message&) {
-        if (mObject) {
-            ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr);
-            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr);
+            ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
+            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
             releaseObject();
         }
     }
@@ -197,8 +166,6 @@
 private:
     JavaVM* mVm;
     jobject mObject;
-    sp<Looper> mLooper;
-    int64_t mFrameNr = -1;
 
     void releaseObject() {
         if (mObject) {
@@ -211,16 +178,14 @@
 class RootRenderNode : public RenderNode, ErrorHandler {
 public:
     explicit RootRenderNode(JNIEnv* env) : RenderNode() {
-        mLooper = Looper::getForThread();
-        LOG_ALWAYS_FATAL_IF(!mLooper.get(),
-                "Must create RootRenderNode on a thread with a looper!");
         env->GetJavaVM(&mVm);
     }
 
     virtual ~RootRenderNode() {}
 
     virtual void onError(const std::string& message) override {
-        mLooper->sendMessage(new RenderingException(mVm, message), 0);
+        JNIEnv* env = getenv(mVm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
     }
 
     virtual void prepareTree(TreeInfo& info) override {
@@ -249,14 +214,6 @@
         info.errorHandler = nullptr;
     }
 
-    void sendMessage(const sp<MessageHandler>& handler) {
-        mLooper->sendMessage(handler, 0);
-    }
-
-    void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
-        mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
-    }
-
     void attachAnimatingNode(RenderNode* animatingNode) {
         mPendingAnimatingRenderNodes.push_back(animatingNode);
     }
@@ -404,7 +361,6 @@
     }
 
 private:
-    sp<Looper> mLooper;
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
     std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
@@ -435,7 +391,9 @@
             // the onFinished callback will then be ignored.
             sp<FinishAndInvokeListener> message
                     = new FinishAndInvokeListener(anim);
-            sendMessageDelayed(message, remainingTimeInMs);
+            auto looper = Looper::getForThread();
+            LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
+            looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
             anim->clearOneShotListener();
         }
     }
@@ -463,7 +421,6 @@
     virtual void runRemainingAnimations(TreeInfo& info) {
         AnimationContext::runRemainingAnimations(info);
         mRootNode->runVectorDrawableAnimators(this, info);
-        postOnFinishedEvents();
     }
 
     virtual void pauseAnimators() override {
@@ -471,27 +428,16 @@
     }
 
     virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
-        OnFinishedEvent event(animator, listener);
-        mOnFinishedEvents.push_back(event);
+        listener->onAnimationFinished(animator);
     }
 
     virtual void destroy() {
         AnimationContext::destroy();
         mRootNode->detachAnimators();
-        postOnFinishedEvents();
     }
 
 private:
     sp<RootRenderNode> mRootNode;
-    std::vector<OnFinishedEvent> mOnFinishedEvents;
-
-    void postOnFinishedEvents() {
-        if (mOnFinishedEvents.size()) {
-            sp<InvokeAnimationListeners> message
-                    = new InvokeAnimationListeners(mOnFinishedEvents);
-            mRootNode->sendMessage(message);
-        }
-    }
 };
 
 class ContextFactoryImpl : public IContextFactory {
@@ -958,7 +904,7 @@
     } else {
         sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
         proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
-            wrapper->postFrameComplete(frameNr);
+            wrapper->onFrameComplete(frameNr);
         });
     }
 }
@@ -1083,6 +1029,10 @@
     proxy->setForceDark(enable);
 }
 
+static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
+    RenderProxy::preload();
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1198,6 +1148,7 @@
     { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
     { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
     { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark },
+    { "preload", "()V", (void*)android_view_ThreadedRenderer_preload },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d04db92..15ceca9 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -25,6 +25,8 @@
 
 #define LOG_TAG "Zygote"
 
+#include <async_safe/log.h>
+
 // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
 #include <sys/mount.h>
 #include <linux/fs.h>
@@ -303,27 +305,23 @@
   int saved_errno = errno;
 
   while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-     // Log process-death status that we care about.  In general it is
-     // not safe to call LOG(...) from a signal handler because of
-     // possible reentrancy.  However, we know a priori that the
-     // current implementation of LOG() is safe to call from a SIGCHLD
-     // handler in the zygote process.  If the LOG() implementation
-     // changes its locking strategy or its use of syscalls within the
-     // lazy-init critical section, its use here may become unsafe.
+     // Log process-death status that we care about.
     if (WIFEXITED(status)) {
-      ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
+      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+                            "Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
     } else if (WIFSIGNALED(status)) {
-      ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
-      if (WCOREDUMP(status)) {
-        ALOGI("Process %d dumped core.", pid);
-      }
+      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+                            "Process %d exited due to signal %d (%s)%s", pid,
+                            WTERMSIG(status), strsignal(WTERMSIG(status)),
+                            WCOREDUMP(status) ? "; core dumped" : "");
     }
 
     // If the just-crashed process is the system_server, bring down zygote
     // so that it is restarted by init and system server will be restarted
     // from there.
     if (pid == gSystemServerPid) {
-      ALOGE("Exit zygote because system server (%d) has terminated", pid);
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                            "Exit zygote because system server (pid %d) has terminated", pid);
       kill(getpid(), SIGKILL);
     }
 
@@ -336,14 +334,17 @@
   // Note that we shouldn't consider ECHILD an error because
   // the secondary zygote might have no children left to wait for.
   if (pid < 0 && errno != ECHILD) {
-    ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG,
+                          "Zygote SIGCHLD error in waitpid: %s", strerror(errno));
   }
 
   if (blastulas_removed > 0) {
     if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) {
       // If this write fails something went terribly wrong.  We will now kill
       // the zygote and let the system bring it back up.
-      ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                            "Zygote failed to write to blastula pool event FD: %s",
+                            strerror(errno));
       kill(getpid(), SIGKILL);
     }
   }
@@ -612,7 +613,7 @@
     }
 }
 
-static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
+static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
     // Create /mnt/user/0/package/<package-name>
     userid_t user_id = multiuser_get_user_id(uid);
     std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
@@ -622,7 +623,7 @@
     CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
 
     StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
-    CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
+    CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn);
 }
 
 static void BindMount(const std::string& sourceDir, const std::string& targetDir,
@@ -642,29 +643,99 @@
                                 fail_fn_t fail_fn) {
     std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
             mntSourceRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntSourceDir, 0755, uid, uid, fail_fn);
 
     std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
             mntTargetRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntTargetDir, 0755, uid, uid, fail_fn);
 
     BindMount(mntSourceDir, mntTargetDir, fail_fn);
 }
 
-
-static void createPkgSpecificDirRoots(const std::string& parentDir,
-                                      bool createSandbox,
-                                      mode_t mode, uid_t uid, gid_t gid,
-                                      fail_fn_t fail_fn) {
-    std::string androidDir = StringPrintf("%s/Android", parentDir.c_str());
-    CreateDir(androidDir, mode, uid, gid, fail_fn);
-    std::vector<std::string> dirs = {"data", "media", "obb"};
-    if (createSandbox) {
-        dirs.push_back("sandbox");
+static void CreateSubDirs(int dirfd, const std::string& parentDirPath,
+                          const std::vector<std::string>& subDirs,
+                          fail_fn_t fail_fn) {
+    for (auto& dirName : subDirs) {
+        struct stat sb;
+        if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) {
+            if (S_ISDIR(sb.st_mode)) {
+                continue;
+            } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s",
+                        parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+            }
+        } else if (errno != ENOENT) {
+            fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1 && errno != EEXIST) {
+            fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
     }
-    for (auto& dir : dirs) {
-        std::string path = StringPrintf("%s/%s", androidDir.c_str(), dir.c_str());
-        CreateDir(path, mode, uid, gid, fail_fn);
+}
+
+static void EnsurePkgSpecificDirs(const std::string& path,
+                                  const std::vector<std::string>& packageNames,
+                                  bool createSandboxDir,
+                                  fail_fn_t fail_fn) {
+    std::string androidDir = StringPrintf("%s/Android", path.c_str());
+    android::base::unique_fd androidFd(
+            open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (androidFd.get() < 0) {
+        if (errno == ENOENT || errno == ENOTDIR) {
+            if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1
+                    && errno != EEXIST) {
+                fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        }
+
+        if (androidFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno)));
+        }
+    }
+
+    std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"};
+    if (createSandboxDir) {
+        dataMediaObbDirs.push_back("sandbox");
+    }
+    CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn);
+    if (createSandboxDir) {
+        dataMediaObbDirs.pop_back();
+    }
+    for (auto& dirName : dataMediaObbDirs) {
+        std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str());
+        android::base::unique_fd dataFd(
+                openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        if (dataFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s",
+                    androidDir.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn);
+    }
+}
+
+static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) {
+
+    struct stat sb;
+    if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) {
+        if (S_ISDIR(sb.st_mode)) {
+            return;
+        } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) {
+            fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                    sandboxSource.c_str(), strerror(errno)));
+        }
+    } else if (errno != ENOENT) {
+        fail_fn(CREATE_ERROR("Failed to stat %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
+    }
+    if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1 && errno != EEXIST) {
+        fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
     }
 }
 
@@ -680,21 +751,21 @@
             StringAppendF(&mntTarget, "/%d", userId);
         }
 
-        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) < 0) {
+        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) {
             ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno));
             continue;
         }
 
-        // Create /mnt/runtime/write/emulated/0/Android/{data,media,obb,sandbox}
-        createPkgSpecificDirRoots(mntSource, true, 0700, AID_ROOT, AID_ROOT, fail_fn);
+        // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn);
 
         std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s",
             mntSource.c_str(), sandboxId.c_str());
-        CreateDir(sandboxSource, 0755, uid, uid, fail_fn);
+        CreatePkgSandboxSource(sandboxSource, fail_fn);
         BindMount(sandboxSource, mntTarget, fail_fn);
 
-        // Create /storage/emulated/0/Android/{data,media,obb}
-        createPkgSpecificDirRoots(mntTarget, false, 0755, uid, uid, fail_fn);
+        // Ensure /storage/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn);
         for (auto& package : packageNames) {
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn);
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn);
@@ -742,7 +813,7 @@
         return;
     }
 
-    if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) {
+    if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) {
         if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) {
             storageSource = (mount_mode == MOUNT_EXTERNAL_FULL)
                     ? "/mnt/runtime/full" : "/mnt/runtime/write";
@@ -775,15 +846,14 @@
             userid_t user_id = multiuser_get_user_id(uid);
             std::string pkgSandboxDir =
                 StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
-            struct stat sb;
             bool sandboxAlreadyCreated = true;
-            if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+            if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) {
                 if (errno == ENOENT) {
                     ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
                     sandboxAlreadyCreated = false;
-                    CreatePkgSandbox(uid, package_name, fail_fn);
+                    CreatePkgSandboxTarget(uid, package_name, fail_fn);
                 } else {
-                    fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+                    fail_fn(CREATE_ERROR("Failed to access %s: %s",
                                          pkgSandboxDir.c_str(), strerror(errno)));
                 }
             }
@@ -794,7 +864,7 @@
                                      pkgSandboxDir.c_str(), strerror(errno)));
             }
 
-            if (access("/storage/obb_mount", F_OK) == 0) {
+            if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) {
                 if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
                     remove("/storage/obb_mount");
                 }
@@ -1288,6 +1358,9 @@
 
   SetSchedulerPolicy(fail_fn);
 
+  __android_log_close();
+  stats_log_close();
+
   const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
   const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
 
@@ -1384,8 +1457,8 @@
       RuntimeAbort(env, __LINE__, "Bad gids array");
     }
 
-    for (int gid_index = gids_num; --gids_num >= 0;) {
-      if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+    for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+      if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
         gid_wakelock_found = true;
         break;
       }
diff --git a/core/jni/runtime_native_boot-flags-test.sh b/core/jni/runtime_native_boot-flags-test.sh
index 66e18bb..01f37f0 100755
--- a/core/jni/runtime_native_boot-flags-test.sh
+++ b/core/jni/runtime_native_boot-flags-test.sh
@@ -172,12 +172,14 @@
   done
 }
 
-# test_android_runtime_flag FLAG VALUE
-# ------------------------------------
-# Test device configuration FLAG with VALUE.
+# test_android_runtime_flag FLAG VALUE GC_RUNTIME_OPTION
+# ------------------------------------------------------
+# Test device configuration FLAG with VALUE. Check that GC_RUNTIME_OPTION is
+# passed as GC Runtime option by the zygote.
 function test_android_runtime_flag {
   local flag=$1
   local value=$2
+  local gc_runtime_option=$3
 
   # Persistent system property (set after a reboot) associated with the device
   # configuration flag.
@@ -196,21 +198,21 @@
   local context="Flag set, before reboot"
   check_device_config_flag "$context" "$flag" "$value"
   check_system_property "$context" "$prop" "$value"
-  check_no_zygote_gc_runtime_option "$context" "$value"
+  check_no_zygote_gc_runtime_option "$context" "$gc_runtime_option"
 
   # Reboot device for the flag value to take effect.
   reboot_and_wait_for_device
   context="Flag set, after 1st reboot"
   check_device_config_flag "$context" "$flag" "$value"
   check_system_property "$context" "$prop" "$value"
-  check_zygote_gc_runtime_option "$context" "$value"
+  check_zygote_gc_runtime_option "$context" "$gc_runtime_option"
 
   # Reboot device a second time and check that the state has persisted.
   reboot_and_wait_for_device
   context="Flag set, after 2nd reboot"
   check_device_config_flag "$context" "$flag" "$value"
   check_system_property "$context" "$prop" "$value"
-  check_zygote_gc_runtime_option "$context" "$value"
+  check_zygote_gc_runtime_option "$context" "$gc_runtime_option"
 
   say "Unsetting device configuration flag..."
   adb shell device_config delete "$namespace" "$flag" >/dev/null
@@ -222,7 +224,7 @@
   context="Flag unset, after 3rd reboot"
   check_no_device_config_flag "$context" "$flag"
   check_no_system_property "$context" "$prop"
-  check_no_zygote_gc_runtime_option "$context" "$value"
+  check_no_zygote_gc_runtime_option "$context" "$gc_runtime_option"
 }
 
 # Enumerate Zygote processes.
@@ -232,9 +234,9 @@
   (zygote32_64|zygote64_32) zygotes="zygote zygote64";;
 esac
 
-# Test "gctype" flag values.
-test_android_runtime_flag gctype nogenerational_cc
-test_android_runtime_flag gctype generational_cc
+# Test "enable_generational_cc" flag values.
+test_android_runtime_flag enable_generational_cc false nogenerational_cc
+test_android_runtime_flag enable_generational_cc true generational_cc
 
 if [[ "$exit_status" -eq 0 ]]; then
   banner "All tests passed."
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 30b9b78..5497b86 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -610,6 +610,11 @@
     // CATEGORY: SETTINGS
     // OS: Q
     ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646;
+
+    // ACTION: An interaction with a Slice or other component in the Panel.
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_PANEL_INTERACTION = 1658;
 }
 
 /**
@@ -2214,4 +2219,16 @@
     // CATEGORY: SETTINGS
     // OS: Q
     SET_NEW_PASSWORD_ACTIVITY = 1644;
+
+    // Panel for Internet Connectivity
+    PANEL_INTERNET_CONNECTIVITY = 1654;
+
+    // Panel for Volume
+    PANEL_VOLUME = 1655;
+
+    // Panel for NFC
+    PANEL_NFC = 1656;
+
+    // Panel for Media Output
+    PANEL_MEDIA_OUTPUT = 1657;
 }
diff --git a/core/proto/android/hardware/sensor/assist/enums.proto b/core/proto/android/hardware/sensor/assist/enums.proto
new file mode 100644
index 0000000..8c5841a
--- /dev/null
+++ b/core/proto/android/hardware/sensor/assist/enums.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.hardware.sensor.assist;
+
+option java_outer_classname = "AssistGestureProtoEnums";
+option java_multiple_files = true;
+
+enum AssistGestureStageEnum {
+  ASSIST_GESTURE_STAGE_UNKNOWN = 0;
+  ASSIST_GESTURE_STAGE_PROGRESS = 1;
+  ASSIST_GESTURE_STAGE_PRIMED = 2;
+  ASSIST_GESTURE_STAGE_DETECTED = 3;
+}
+
+enum AssistGestureFeedbackEnum {
+  ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0;
+  ASSIST_GESTURE_FEEDBACK_NOT_USED = 1;
+  ASSIST_GESTURE_FEEDBACK_USED = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index a4167c1..516fa7b 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -58,6 +58,10 @@
         optional int64 duration_ms = 2;
     }
     repeated TxLevel tx = 4;
+
+    // Total rail charge consumed by the monitored rails by the controller. The value may
+    // always be 0 if the device doesn't support monitored rail calculations.
+    optional double monitored_rail_charge_mah = 5;
 }
 
 message SystemProto {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d577653..cccb40d 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -448,6 +448,13 @@
         optional SettingProto game_driver_whitelist = 12;
         // ANGLE - List of Apps that can check ANGLE rules
         optional SettingProto angle_whitelist = 13;
+        // Game Driver - List of blacklists, each blacklist is a blacklist for
+        // a specific Game Driver version
+        optional SettingProto game_driver_blacklists = 14;
+        // ANGLE - Show a dialog box when ANGLE is selected for the currently running PKG
+        optional SettingProto show_angle_in_use_dialog = 15;
+        // Game Driver - List of libraries in sphal accessible by Game Driver
+        optional SettingProto game_driver_sphal_libraries = 16;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp
new file mode 100644
index 0000000..c0ac2cb
--- /dev/null
+++ b/core/proto/android/server/connectivity/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 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.
+
+java_library_static {
+    name: "datastallprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "data_stall_event.proto",
+    ],
+    sdk_version: "system_current",
+    no_framework_libs: true,
+}
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_file_copy.xml b/core/res/res/drawable/ic_file_copy.xml
new file mode 100644
index 0000000..b6d5e73
--- /dev/null
+++ b/core/res/res/drawable/ic_file_copy.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5l6,6v10c0,1.1 -0.9,2 -2,2L7.99,23C6.89,23 6,22.1 6,21l0.01,-14c0,-1.1 0.89,-2 1.99,-2h7zM14,12h5.5L14,6.5L14,12z"
+      android:fillColor="#FF737373"/>
+</vector>
diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml
index 35907cc..a4755ee 100644
--- a/core/res/res/drawable/ic_qs_night_display_on.xml
+++ b/core/res/res/drawable/ic_qs_night_display_on.xml
@@ -1,5 +1,5 @@
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    Copyright (C) 2019 The Android Open Source Project
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="64dp"
-    android:height="64dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <group
-        android:translateX="-1.0">
-        <path
-            android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z"
-            android:fillColor="#FFF"/>
-    </group>
-</vector>
\ No newline at end of file
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M6.28,4.81c0,0.16,0.01,0.32,0.02,0.48c0.38,6.58,5.83,12.03,12.41,12.41c0.16,0.01,0.32,0.02,0.47,0.02 c-1.58,1.2-3.53,1.88-5.56,1.88c-0.46,0-0.93-0.03-1.4-0.1c-3.96-0.58-7.13-3.75-7.71-7.71C4.13,9.24,4.8,6.75,6.28,4.81 M8.27,0.6 c-0.08,0-0.17,0.02-0.25,0.07c-3.8,2.2-6.2,6.56-5.49,11.4c0.7,4.82,4.59,8.7,9.4,9.4c0.57,0.08,1.13,0.12,1.69,0.12 c4.15,0,7.78-2.26,9.72-5.62c0.2-0.35-0.07-0.76-0.44-0.76c-0.05,0-0.1,0.01-0.15,0.02c-1.03,0.31-2.12,0.48-3.25,0.48 c-0.22,0-0.44-0.01-0.67-0.02C13.23,15.38,8.62,10.77,8.29,5.17C8.21,3.81,8.38,2.49,8.75,1.26C8.86,0.91,8.59,0.6,8.27,0.6 L8.27,0.6z" />
+</vector>
+
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 14a5310..10798ad 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,12 +20,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="288dp"
     android:maxCollapsedHeightSmall="56dp"
     android:id="@id/contentPanel">
 
-
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -153,8 +151,9 @@
         android:background="?attr/colorBackgroundFloating">
 
         <LinearLayout
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/chooser_preview_width"
             android:layout_height="wrap_content"
+            android:layout_gravity="center"
             android:orientation="horizontal"
             android:paddingLeft="@dimen/chooser_edge_margin_normal"
             android:paddingRight="@dimen/chooser_edge_margin_normal"
@@ -182,8 +181,9 @@
         <!-- Required sub-layout so we can get the nice rounded corners-->
         <!-- around this section -->
         <LinearLayout
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/chooser_preview_width"
             android:layout_height="wrap_content"
+            android:layout_gravity="center"
             android:orientation="horizontal"
             android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
             android:layout_marginRight="@dimen/chooser_edge_margin_thin"
@@ -213,6 +213,66 @@
         </LinearLayout>
     </LinearLayout>
 
+    <!-- Layout Option 3: File preview, icon, filename, copy-->
+    <LinearLayout
+        android:id="@+id/content_preview_file_area"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingBottom="@dimen/chooser_view_spacing"
+        android:visibility="gone"
+        android:background="?attr/colorBackgroundFloating">
+
+        <LinearLayout
+            android:layout_width="@dimen/chooser_preview_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:orientation="horizontal"
+            android:paddingLeft="@dimen/chooser_edge_margin_normal"
+            android:paddingRight="@dimen/chooser_edge_margin_normal"
+            android:layout_marginBottom="@dimen/chooser_view_spacing"
+            android:id="@+id/content_preview_file_layout">
+
+            <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+                  android:id="@+id/content_preview_file_thumbnail"
+                  android:layout_width="75dp"
+                  android:layout_height="75dp"
+                  android:layout_marginRight="16dp"
+                  android:adjustViewBounds="true"
+                  android:layout_gravity="center_vertical"
+                  android:gravity="center"
+                  android:scaleType="centerCrop"
+                  android:visibility="gone"/>
+            <ImageView
+                  android:id="@+id/content_preview_file_icon"
+                  android:layout_width="36dp"
+                  android:layout_height="36dp"
+                  android:layout_marginRight="16dp"
+                  android:adjustViewBounds="true"
+                  android:layout_gravity="center_vertical"
+                  android:gravity="center"
+                  android:scaleType="fitCenter"
+                  android:visibility="gone"/>
+            <TextView
+                android:id="@+id/content_preview_filename"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:ellipsize="middle"
+                android:gravity="start|top"
+                android:paddingRight="24dp"
+                android:singleLine="true"/>
+            <Button
+                android:id="@+id/file_copy_button"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:gravity="center"
+                android:layout_gravity="center_vertical"
+                android:background="@drawable/ic_content_copy_gm2"/>
+        </LinearLayout>
+    </LinearLayout>
+
     <ListView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/core/res/res/layout/notification_material_media_seekbar.xml b/core/res/res/layout/notification_material_media_seekbar.xml
index 1b691d6..c23ca83 100644
--- a/core/res/res/layout/notification_material_media_seekbar.xml
+++ b/core/res/res/layout/notification_material_media_seekbar.xml
@@ -30,6 +30,7 @@
         android:maxHeight="3dp"
         android:paddingTop="24dp"
         android:paddingBottom="24dp"
+        android:clickable="true"
         android:layout_marginBottom="-24dp"
         android:layout_marginTop="-12dp"
         android:splitTrack="false"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 64d91ad..13fef67 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -66,7 +66,7 @@
                 android:id="@+id/media_actions"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|end"
+                android:layout_gravity="top|end"
                 android:layout_marginStart="10dp"
                 android:layoutDirection="ltr"
                 android:orientation="horizontal"
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 351bd81..9e87a47 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -76,4 +76,6 @@
      <!-- Floating toolbar dimensions -->
      <dimen name="floating_toolbar_preferred_width">544dp</dimen>
 
+    <dimen name="chooser_preview_width">480dp</dimen>
+
 </resources>
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 949c12e..0721f6f 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -65,4 +65,7 @@
     <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
 
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" />
+
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent" />
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 46e14b4..088669d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4349,14 +4349,18 @@
         <attr name="indeterminate" format="boolean" />
         <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
         <attr name="indeterminateOnly" format="boolean" />
-        <!-- Drawable used for the indeterminate mode. -->
+        <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more
+             control over the animation.-->
         <attr name="indeterminateDrawable" format="reference" />
         <!-- Drawable used for the progress mode. -->
         <attr name="progressDrawable" format="reference" />
-        <!-- Duration of the indeterminate animation. -->
+        <!-- Duration of the indeterminate animation. Only affects the indeterminate animation
+             if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateDuration" format="integer" min="1" />
-        <!-- Defines how the indeterminate mode should behave when the progress
-        reaches max. -->
+        <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only
+             affects the indeterminate animation if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateBehavior">
             <!-- Progress starts over from 0. -->
             <enum name="repeat" value="1" />
@@ -4367,6 +4371,9 @@
         <attr name="maxWidth" />
         <attr name="minHeight" format="dimension" />
         <attr name="maxHeight" />
+        <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear
+             interpolation. Only affects the indeterminate animation if the indeterminate Drawable
+             does not implement android.graphics.drawable.Animatable.-->
         <attr name="interpolator" format="reference" />
         <!-- Timeout between frames of animation in milliseconds.
              {@deprecated Not used by the framework}. -->
@@ -7997,7 +8004,8 @@
 
         <!-- Indicates that this wallpaper service can support multiple engines to render on each
              surface independently. An example use case is a multi-display set-up where the
-             wallpaper service can render surfaces to each of the connected displays. -->
+             wallpaper service can render surfaces to each of the connected displays. Corresponds to
+             {@link android.app.WallpaperInfo#supportsMultipleDisplays()} -->
         <attr name="supportsMultipleDisplays" format="boolean" />
 
     </declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d14164f..e65e7da 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -46,6 +46,8 @@
         <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_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_location</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>
@@ -55,8 +57,6 @@
         <item><xliff:g id="id">@string/status_bar_hotspot</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_microphone</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
@@ -869,6 +869,9 @@
          which means to get a larger screen. -->
     <bool name="config_lidControlsDisplayFold">false</bool>
 
+    <!-- Indicate the display area rect for foldable devices in folded state. -->
+    <string name="config_foldedArea"></string>
+
     <!-- Desk dock behavior -->
 
     <!-- The number of degrees to rotate the display when the device is in a desk dock.
@@ -3252,7 +3255,7 @@
          skinny aspect ratio that is not expected to be widely used. -->
     <item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.41841004184</item>
 
-    <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any
+    <!-- The maximum aspect ratio (width/height) that is supported for picture-in-picture. Any
          ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. -->
     <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item>
 
@@ -3273,6 +3276,11 @@
     -->
     <integer name="config_dockedStackDividerSnapMode">0</integer>
 
+    <!-- The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. If
+         config_forceDefaultOrientation is set to true, the rotation on a close-to-square display
+         will be fixed. -->
+    <item name="config_closeToSquareDisplayMaxAspectRatio" format="float" type="dimen">1.333</item>
+
     <!-- List of comma separated package names for which we the system will not show crash, ANR,
          etc. dialogs. -->
     <string translatable="false" name="config_appsNotReportingCrashes"></string>
@@ -3906,19 +3914,19 @@
 
     <!-- See DisplayWhiteBalanceController.
          A float array containing a list of ambient color temperatures, in Kelvin. This array,
-         together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a
+         together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
          lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
          ambient color temperature readings to a target color temperature for the display.
          This table is optional. If used, this array must,
          1) Contain at least two entries
-         2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. -->
-    <array name="config_displayWhiteBalanceAmbientTemperatureValues">
+         2) Be the same length as config_displayWhiteBalanceDisplayColorTemperatures. -->
+    <array name="config_displayWhiteBalanceAmbientColorTemperatures">
     </array>
 
     <!-- See DisplayWhiteBalanceController.
          An array containing a list of display color temperatures, in Kelvin. See
-         config_displayWhiteBalanceAmbientTemperatureValues for additional details.
+         config_displayWhiteBalanceAmbientColorTemperatures for additional details.
          The same restrictions apply to this array. -->
-    <array name="config_displayWhiteBalanceDisplayTemperatureValues">
+    <array name="config_displayWhiteBalanceDisplayColorTemperatures">
     </array>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9f86f84..39cbd26 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -721,4 +721,5 @@
     <dimen name="chooser_edge_margin_thin">16dp</dimen>
     <dimen name="chooser_edge_margin_normal">24dp</dimen>
     <dimen name="chooser_preview_image_font_size">20sp</dimen>
+    <dimen name="chooser_preview_width">-1px</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5e65605..3580dd4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2951,6 +2951,8 @@
     <public-group type="style" first-id="0x010302e2">
         <!-- @hide @SystemApi -->
         <public name="Theme.DeviceDefault.DocumentsUI" />
+        <public name="Theme.DeviceDefault.DayNight" />
+        <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" />
     </public-group>
 
     <public-group type="id" first-id="0x01020046">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 62e79da..09fb663 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3712,7 +3712,7 @@
     <string name="ext_media_browse_action">Explore</string>
 
     <!-- Notification action to transfer media [CHAR LIMIT=40] -->
-    <string name="ext_media_seamless_action">Seamless transfer</string>
+    <string name="ext_media_seamless_action">Switch output</string>
 
     <!-- Notification title when external media is missing [CHAR LIMIT=30] -->
     <string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string>
@@ -4291,6 +4291,13 @@
     <!-- Title text to append when the display is secure.  [CHAR LIMIT=30] -->
     <string name="display_manager_overlay_display_secure_suffix">, secure</string>
 
+    <!-- Activity starter -->
+    <!-- Toast message for blocking background activity starts feature running in permissive mode -->
+    <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See go/q-bg-block.</string>
+
+    <!-- Toast message for blocking background activity starts feature running in enforcing mode -->
+    <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See go/q-bg-block. </string>
+
     <!-- Keyguard strings -->
     <!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
     <string name="kg_forgot_pattern_button_text">Forgot Pattern</string>
@@ -5284,4 +5291,9 @@
     <!-- Strings for car -->
     <!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
     <string name="car_loading_profile">Loading</string>
+
+    <plurals name="file_count">
+        <item quantity="one"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> file</item>
+        <item quantity="other"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> files</item>
+    </plurals>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a7d71f..1ebd6e9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -50,6 +50,11 @@
   <java-symbol type="id" name="characterPicker" />
   <java-symbol type="id" name="clearDefaultHint" />
   <java-symbol type="id" name="contentPanel" />
+  <java-symbol type="id" name="content_preview_file_area" />
+  <java-symbol type="id" name="content_preview_file_icon" />
+  <java-symbol type="id" name="content_preview_file_layout" />
+  <java-symbol type="id" name="content_preview_file_thumbnail" />
+  <java-symbol type="id" name="content_preview_filename" />
   <java-symbol type="id" name="content_preview_image_area" />
   <java-symbol type="id" name="content_preview_image_1_large" />
   <java-symbol type="id" name="content_preview_image_2_large" />
@@ -62,6 +67,7 @@
   <java-symbol type="id" name="content_preview_title" />
   <java-symbol type="id" name="content_preview_title_layout" />
   <java-symbol type="id" name="copy_button" />
+  <java-symbol type="id" name="file_copy_button" />
   <java-symbol type="id" name="current_scene" />
   <java-symbol type="id" name="scene_layoutid_cache" />
   <java-symbol type="id" name="customPanel" />
@@ -397,6 +403,7 @@
   <java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
   <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
   <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
+  <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -756,6 +763,8 @@
   <java-symbol type="string" name="display_manager_hdmi_display_name" />
   <java-symbol type="string" name="display_manager_overlay_display_name" />
   <java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
+  <java-symbol type="string" name="activity_starter_block_bg_activity_starts_permissive" />
+  <java-symbol type="string" name="activity_starter_block_bg_activity_starts_enforcing" />
   <java-symbol type="string" name="display_manager_overlay_display_title" />
   <java-symbol type="string" name="double_tap_toast" />
   <java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
@@ -1266,6 +1275,7 @@
   <java-symbol type="string" name="tooltip_popup_title" />
 
   <java-symbol type="plurals" name="bugreport_countdown" />
+  <java-symbol type="plurals" name="file_count" />
   <java-symbol type="plurals" name="last_num_days" />
   <java-symbol type="plurals" name="matches_found" />
   <java-symbol type="plurals" name="restr_pin_countdown" />
@@ -1307,6 +1317,7 @@
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="default_lock_wallpaper" />
   <java-symbol type="drawable" name="indicator_input_error" />
+  <java-symbol type="drawable" name="ic_file_copy" />
   <java-symbol type="drawable" name="popup_bottom_dark" />
   <java-symbol type="drawable" name="popup_bottom_bright" />
   <java-symbol type="drawable" name="popup_bottom_medium" />
@@ -2747,6 +2758,7 @@
   <java-symbol type="dimen" name="chooser_edge_margin_thin" />
   <java-symbol type="dimen" name="chooser_edge_margin_normal" />
   <java-symbol type="dimen" name="chooser_preview_image_font_size"/>
+  <java-symbol type="dimen" name="chooser_preview_width" />
   <java-symbol type="layout" name="chooser_grid" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="day_picker_view_pager" />
@@ -3576,7 +3588,10 @@
   <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
 
   <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
+
+  <!-- For Foldables -->
   <java-symbol type="bool" name="config_lidControlsDisplayFold" />
+  <java-symbol type="string" name="config_foldedArea" />
 
   <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
   <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
@@ -3643,7 +3658,7 @@
   <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
-  <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" />
-  <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" />
+  <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
   <java-symbol type="drawable" name="ic_action_open" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 1603508..194c86c 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1652,6 +1652,7 @@
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
 
+    <!-- DeviceDefault theme for day/night activities. -->
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" />
 
     <!-- Theme used for the intent picker activity. -->
@@ -1697,6 +1698,10 @@
         <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
+    <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" />
+
     <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar">
         <item name="colorAccent">@color/accent_device_default_dark</item>
     </style>
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index a33de7b..a71000b 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -182,8 +182,7 @@
 
         uri = Uri.parse("http://bob%40lee%3ajr@local%68ost:4%32");
         assertEquals("bob@lee:jr", uri.getUserInfo());
-        assertEquals("localhost", uri.getHost());
-        assertEquals(42, uri.getPort());
+        assertEquals("localhost:42", uri.getHost());
 
         uri = Uri.parse("http://localhost");
         assertEquals("localhost", uri.getHost());
diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
index 02a9adf..bff6b5f 100644
--- a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
@@ -60,7 +60,8 @@
                 DocumentsContract.buildDocumentUri(TestDocumentsProvider.AUTHORITY, DOCUMENT_ID);
         try (ContentProviderClient client =
                      mResolver.acquireUnstableContentProviderClient(docUri)) {
-            final Path actual = DocumentsContract.findDocumentPath(client, docUri);
+            final Path actual = DocumentsContract.findDocumentPath(
+                    ContentResolver.wrap(client), docUri);
             assertEquals(expected, actual);
         }
     }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 206682a..46cac7a 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -122,6 +122,7 @@
                     Settings.Global.APP_OPS_CONSTANTS,
                     Settings.Global.APP_STANDBY_ENABLED,
                     Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE,
+                    Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE,
                     Settings.Global.ASSISTED_GPS_ENABLED,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
                     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
@@ -491,8 +492,11 @@
                     Settings.Global.GAME_DRIVER_ALL_APPS,
                     Settings.Global.GAME_DRIVER_OPT_IN_APPS,
                     Settings.Global.GAME_DRIVER_OPT_OUT_APPS,
+                    Settings.Global.GAME_DRIVER_BLACKLISTS,
                     Settings.Global.GAME_DRIVER_BLACKLIST,
                     Settings.Global.GAME_DRIVER_WHITELIST,
+                    Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES,
+                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
@@ -619,6 +623,7 @@
                  Settings.Secure.DISABLED_PRINT_SERVICES,
                  Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
                  Settings.Secure.DISPLAY_DENSITY_FORCED,
+                 Settings.Secure.DOCKED_CLOCK_FACE,
                  Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                  Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
                  Settings.Secure.ENABLED_INPUT_METHODS,  // Intentionally removed in P
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index f325d89..a5ac270 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -15,6 +15,7 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
@@ -23,6 +24,8 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.content.LocusId;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.SystemClock;
 import android.view.autofill.AutofillId;
@@ -44,9 +47,11 @@
 
     private static final long MY_EPOCH = SystemClock.uptimeMillis();
 
+    private static final LocusId ID = new LocusId(Uri.parse("WHATEVER"));
+
     // Not using @Mock because it's final - no need to be fancy here....
-    private final ContentCaptureContext mClientContext = new ContentCaptureContext.Builder()
-            .setAction("WHATEVER").build();
+    private final ContentCaptureContext mClientContext =
+            new ContentCaptureContext.Builder(ID).build();
 
     @Test
     public void testSetAutofillId_null() {
@@ -174,8 +179,9 @@
         assertThat(event.getIds()).isNull();
         assertThat(event.getText()).isNull();
         assertThat(event.getViewNode()).isNull();
-        final ContentCaptureContext clientContext = event.getClientContext();
-        assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
+        final ContentCaptureContext clientContext = event.getContentCaptureContext();
+        assertThat(clientContext).isNotNull();
+        assertThat(clientContext.getLocusId()).isEqualTo(ID);
     }
 
     @Test
@@ -205,9 +211,43 @@
         assertThat(event.getIds()).isNull();
         assertThat(event.getText()).isNull();
         assertThat(event.getViewNode()).isNull();
-        assertThat(event.getClientContext()).isNull();
+        assertThat(event.getContentCaptureContext()).isNull();
     }
 
+    @Test
+    public void testContextUpdated_directly() {
+        final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+                .setClientContext(mClientContext);
+        assertThat(event).isNotNull();
+        assertContextUpdatedEvent(event);
+    }
+
+    @Test
+    public void testContextUpdated_throughParcel() {
+        final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+                .setClientContext(mClientContext);
+        assertThat(event).isNotNull();
+        final ContentCaptureEvent clone = cloneThroughParcel(event);
+        assertContextUpdatedEvent(clone);
+    }
+
+    private void assertContextUpdatedEvent(ContentCaptureEvent event) {
+        assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED);
+        assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
+        assertThat(event.getSessionId()).isEqualTo("42");
+        assertThat(event.getParentSessionId()).isNull();
+        assertThat(event.getId()).isNull();
+        assertThat(event.getIds()).isNull();
+        assertThat(event.getText()).isNull();
+        assertThat(event.getViewNode()).isNull();
+        final ContentCaptureContext clientContext = event.getContentCaptureContext();
+        assertThat(clientContext).isNotNull();
+        assertThat(clientContext.getLocusId()).isEqualTo(ID);
+    }
+
+    // TODO(b/123036895): add test for all events type (right now we're just testing the 3 types
+    // that use logic to write to parcel
+
     private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) {
         Parcel parcel = Parcel.obtain();
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 34fdebf..b6717e1 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -160,5 +160,10 @@
         public void internalNotifyViewHierarchyEvent(boolean started) {
             throw new UnsupportedOperationException("should not have been called");
         }
+
+        @Override
+        public void updateContentCaptureContext(ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 3d59835a..f27f3f9 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -30,6 +30,7 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -41,6 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -409,8 +411,7 @@
     @Test
     public void copyTextToClipboard() throws Exception {
         Intent sendIntent = createSendTextIntent();
-        List<ResolvedComponentInfo> resolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(1);
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
         when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
             Mockito.anyBoolean(),
@@ -421,6 +422,7 @@
                 .launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
 
+        onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
         onView(withId(R.id.copy_button)).perform(click());
         ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
                 Context.CLIPBOARD_SERVICE);
@@ -439,8 +441,9 @@
         ArrayList<Uri> uris = new ArrayList<>();
         uris.add(uri);
 
-        Intent sendIntent = createSendImageIntentWithPreview(uris);
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
         sOverrides.previewThumbnail = createBitmap();
+        sOverrides.isImageType = true;
 
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
@@ -464,8 +467,9 @@
         uris.add(uri);
         uris.add(uri);
 
-        Intent sendIntent = createSendImageIntentWithPreview(uris);
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
         sOverrides.previewThumbnail = createBitmap();
+        sOverrides.isImageType = true;
 
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
@@ -492,8 +496,9 @@
         uris.add(uri);
         uris.add(uri);
 
-        Intent sendIntent = createSendImageIntentWithPreview(uris);
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
         sOverrides.previewThumbnail = createBitmap();
+        sOverrides.isImageType = true;
 
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
@@ -538,12 +543,11 @@
         ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
         mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test"));
         waitForIdle();
-        verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
+
+        verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
-        assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
-        assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
-                is(CONTENT_PREVIEW_TEXT));
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
     }
 
     @Test
@@ -552,9 +556,17 @@
 
         MetricsLogger mockLogger = sOverrides.metricsLogger;
         ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+            Mockito.anyBoolean(),
+            Mockito.anyBoolean(),
+            Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
         mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
-        verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
+        verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
         assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
                 is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
@@ -570,8 +582,9 @@
         ArrayList<Uri> uris = new ArrayList<>();
         uris.add(uri);
 
-        Intent sendIntent = createSendImageIntentWithPreview(uris);
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
         sOverrides.previewThumbnail = createBitmap();
+        sOverrides.isImageType = true;
 
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
@@ -595,6 +608,104 @@
                 is(CONTENT_PREVIEW_IMAGE));
     }
 
+    @Test
+    public void oneVisibleFilePreview() throws InterruptedException {
+        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
+
+        ArrayList<Uri> uris = new ArrayList<>();
+        uris.add(uri);
+
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
+        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
+        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+    }
+
+
+    @Test
+    public void moreThanOneVisibleFilePreview() throws InterruptedException {
+        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
+
+        ArrayList<Uri> uris = new ArrayList<>();
+        uris.add(uri);
+        uris.add(uri);
+        uris.add(uri);
+
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
+        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 2 files")));
+        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void contentProviderThrowSecurityException() throws InterruptedException {
+        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
+
+        ArrayList<Uri> uris = new ArrayList<>();
+        uris.add(uri);
+
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        sOverrides.resolverForceException = true;
+
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
+        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
+        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void contentProviderReturnsNoColumns() throws InterruptedException {
+        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
+
+        ArrayList<Uri> uris = new ArrayList<>();
+        uris.add(uri);
+        uris.add(uri);
+
+        Intent sendIntent = createSendUriIntentWithPreview(uris);
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        Cursor cursor = mock(Cursor.class);
+        when(cursor.getCount()).thenReturn(1);
+        Mockito.doNothing().when(cursor).close();
+        when(cursor.moveToFirst()).thenReturn(true);
+        when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1);
+
+        sOverrides.resolverCursor = cursor;
+
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
+        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 1 file")));
+        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+    }
+
     private Intent createSendTextIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -615,7 +726,7 @@
         return sendIntent;
     }
 
-    private Intent createSendImageIntentWithPreview(ArrayList<Uri> uris) {
+    private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) {
         Intent sendIntent = new Intent();
 
         if (uris.size() > 1) {
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index f60467b..57c84ff 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -19,8 +19,10 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.usage.UsageStatsManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.util.Size;
@@ -89,11 +91,7 @@
 
     @Override
     protected boolean isImageType(String mimeType) {
-        if (sOverrides.previewThumbnail != null) {
-            return true;
-        }
-
-        return super.isImageType(mimeType);
+        return sOverrides.isImageType;
     }
 
     @Override
@@ -101,6 +99,19 @@
         return sOverrides.metricsLogger;
     }
 
+    @Override
+    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
+        if (sOverrides.resolverCursor != null) {
+            return sOverrides.resolverCursor;
+        }
+
+        if (sOverrides.resolverForceException) {
+            throw new SecurityException("Test exception handling");
+        }
+
+        return super.queryResolver(resolver, uri);
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -112,6 +123,9 @@
         public Function<TargetInfo, Boolean> onSafelyStartCallback;
         public ResolverListController resolverListController;
         public Boolean isVoiceInteraction;
+        public boolean isImageType;
+        public Cursor resolverCursor;
+        public boolean resolverForceException;
         public Bitmap previewThumbnail;
         public MetricsLogger metricsLogger;
 
@@ -120,6 +134,9 @@
             isVoiceInteraction = null;
             createPackageManager = null;
             previewThumbnail = null;
+            isImageType = false;
+            resolverCursor = null;
+            resolverForceException = false;
             resolverListController = mock(ResolverListController.class);
             metricsLogger = mock(MetricsLogger.class);
         }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 64b7c2c..1c84829 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -124,7 +124,7 @@
 
         // Get thread data from KernelCpuThreadReader
         final KernelCpuThreadReader kernelCpuThreadReader =
-                KernelCpuThreadReader.create(8, uid -> uid == Process.myUid());
+                KernelCpuThreadReader.create(8, uid -> uid == Process.myUid(), 0);
         assertNotNull(kernelCpuThreadReader);
         final ProcessCpuUsage currentProcessCpuUsage =
                 kernelCpuThreadReader.getCurrentProcessCpuUsage();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b9744f5..442ece5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -103,6 +103,7 @@
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 8,
                 uid -> 1000 <= uid && uid < 2000,
+                0,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
                 processUtils);
@@ -144,6 +145,7 @@
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 8,
                 uidPredicate,
+                0,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
                 processUtils);
@@ -162,6 +164,60 @@
         }
     }
 
+    @Test
+    public void testReader_filtersLowUsage() throws IOException {
+        int[] uids = new int[]{0, 1, 2, 3, 4};
+        int[] cpuUsage = new int[]{10, 0, 2, 100, 3};
+        int[] expectedUids = new int[]{0, 3, 4};
+        Predicate<Integer> uidPredicate = uid -> true;
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return pid;
+                    }
+                };
+
+        for (int i = 0; i < uids.length; i++) {
+            int uid = uids[i];
+            setupDirectory(
+                    mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                    new int[]{uid * 10},
+                    "process" + uid,
+                    new String[]{"thread" + uid},
+                    new int[]{1000},
+                    new int[][]{{cpuUsage[i]}});
+        }
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                8,
+                uidPredicate,
+                30,
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+                processUtils);
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+                kernelCpuThreadReader.getProcessCpuUsageByUids();
+        processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.uid));
+
+        assertEquals(expectedUids.length, processCpuUsageByUids.size());
+        for (int i = 0; i < expectedUids.length; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                    processCpuUsageByUids.get(i);
+            assertEquals(expectedUids[i], processCpuUsage.uid);
+        }
+
+    }
+
     private void setupDirectory(Path processPath, int[] threadIds, String processName,
             String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
         // Make /proc/$PID
@@ -328,7 +384,6 @@
                         new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
     }
 
-
     @Test
     public void testGetBigFrequenciesStartIndex_simple() {
         assertEquals(
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 7382213..f19e44c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -207,7 +207,7 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.mainline.networkstack">
+    <privapp-permissions package="com.android.networkstack">
         <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
@@ -328,4 +328,9 @@
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.dynandroid">
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.MANAGE_DYNAMIC_ANDROID"/>
+    </privapp-permissions>
+
 </permissions>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 18f0cae..8135671 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -891,8 +891,10 @@
             }
         }
 
+        ColorSpace cs = source.getColorSpace();
+
         if (m == null || m.isIdentity()) {
-            bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha());
+            bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs);
             paint = null;   // not needed
         } else {
             final boolean transformed = !m.rectStaysRect();
@@ -906,9 +908,14 @@
             if (transformed) {
                 if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) {
                     transformedConfig = Config.ARGB_8888;
+                    if (cs == null) {
+                        cs = ColorSpace.get(ColorSpace.Named.SRGB);
+                    }
                 }
             }
-            bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
+
+            bitmap = createBitmap(null, neww, newh, transformedConfig,
+                    transformed || source.hasAlpha(), cs);
 
             paint = new Paint();
             paint.setFilterBitmap(filter);
@@ -917,8 +924,6 @@
             }
         }
 
-        nativeCopyColorSpace(source.mNativePtr, bitmap.mNativePtr);
-
         // The new bitmap was created from a known bitmap source so assume that
         // they use the same density
         bitmap.mDensity = source.mDensity;
@@ -1000,10 +1005,10 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
-     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
-     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
-     *                   is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
+     *                   and {@link ColorSpace.Named#SRGB sRGB} or
+     *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
+     *                   corresponding extended range variant is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
@@ -1055,10 +1060,10 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
-     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
-     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
-     *                   is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
+     *                   and {@link ColorSpace.Named#SRGB sRGB} or
+     *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
+     *                   corresponding extended range variant is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
@@ -1075,22 +1080,12 @@
         if (config == Config.HARDWARE) {
             throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
         }
-        if (colorSpace == null) {
+        if (colorSpace == null && config != Config.ALPHA_8) {
             throw new IllegalArgumentException("can't create bitmap without a color space");
         }
 
-        if (config != Config.ARGB_8888) {
-            if (config == Config.RGBA_F16) {
-                // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See
-                // b/120960866. SRGB matches the old (incorrect) behavior.
-                //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
-                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            } else {
-                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            }
-        }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
-                colorSpace.getNativeInstance());
+                colorSpace == null ? 0 : colorSpace.getNativeInstance());
 
         if (display != null) {
             bm.mDensity = display.densityDpi;
@@ -1514,6 +1509,9 @@
      * Convenience method that returns the width of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled width of this bitmap, according to the density scale factor.
      */
@@ -1525,6 +1523,9 @@
      * Convenience method that returns the height of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled height of this bitmap, according to the density scale factor.
      */
@@ -1701,41 +1702,9 @@
     @Nullable
     public final ColorSpace getColorSpace() {
         checkRecycled("getColorSpace called on a recycled bitmap");
-        // Cache the color space retrieval since it can be fairly expensive
         if (mColorSpace == null) {
-            if (nativeIsConfigF16(mNativePtr)) {
-                // an F16 bitmaps is intended to always be linear extended, but due to
-                // inconsistencies in Bitmap.create() functions it is possible to have
-                // rendered into a bitmap in non-linear sRGB.
-                if (nativeIsSRGB(mNativePtr)) {
-                    mColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
-                } else {
-                    mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
-                }
-            } else if (nativeIsSRGB(mNativePtr)) {
-                mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            } else if (nativeIsSRGBLinear(mNativePtr)) {
-                mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
-            } else {
-                float[] xyz = new float[9];
-                float[] params = new float[7];
-
-                boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params);
-                if (hasColorSpace) {
-                    ColorSpace.Rgb.TransferParameters parameters =
-                            new ColorSpace.Rgb.TransferParameters(
-                                    params[0], params[1], params[2],
-                                    params[3], params[4], params[5], params[6]);
-                    ColorSpace cs = ColorSpace.match(xyz, parameters);
-                    if (cs != null) {
-                        mColorSpace = cs;
-                    } else {
-                        mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters);
-                    }
-                }
-            }
+            mColorSpace = nativeComputeColorSpace(mNativePtr);
         }
-
         return mColorSpace;
     }
 
@@ -1749,6 +1718,9 @@
      *         components min/max values reduce the numerical range compared to the
      *         previously assigned color space.
      *
+     * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()})
+     *         is {@link Config#ALPHA_8}.
+     *
      * @param colorSpace to assign to the bitmap
      */
     public void setColorSpace(@NonNull ColorSpace colorSpace) {
@@ -1756,29 +1728,47 @@
         if (colorSpace == null) {
             throw new IllegalArgumentException("The colorSpace cannot be set to null");
         }
-        if (getColorSpace() != null) {
-            if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) {
-                throw new IllegalArgumentException("The new ColorSpace must have the same "
-                        + "component count as the current ColorSpace");
-            }
-            for (int i = 0; i < mColorSpace.getComponentCount(); i++) {
-                if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) {
-                    throw new IllegalArgumentException("The new ColorSpace cannot increase the "
-                            + "minimum value for any of the components compared to the current "
-                            + "ColorSpace. To perform this type of conversion create a new Bitmap "
-                            + "in the desired ColorSpace and draw this Bitmap into it.");
-                }
-                if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) {
-                    throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
-                            + "maximum value for any of the components compared to the current "
-                            + "ColorSpace/ To perform this type of conversion create a new Bitmap"
-                            + "in the desired ColorSpace and draw this Bitmap into it.");
-                }
-            }
+
+        if (getConfig() == Config.ALPHA_8) {
+            throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8");
         }
 
+        // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an
+        // Exception.
+        final ColorSpace oldColorSpace = getColorSpace();
         nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance());
-        mColorSpace = colorSpace;
+
+        // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we
+        // corrected it because the Bitmap is F16.
+        mColorSpace = null;
+        final ColorSpace newColorSpace = getColorSpace();
+
+        try {
+            if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) {
+                throw new IllegalArgumentException("The new ColorSpace must have the same "
+                        + "component count as the current ColorSpace");
+            } else {
+                for (int i = 0; i < oldColorSpace.getComponentCount(); i++) {
+                    if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) {
+                        throw new IllegalArgumentException("The new ColorSpace cannot increase the "
+                                + "minimum value for any of the components compared to the current "
+                                + "ColorSpace. To perform this type of conversion create a new "
+                                + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
+                    }
+                    if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) {
+                        throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
+                                + "maximum value for any of the components compared to the current "
+                                + "ColorSpace/ To perform this type of conversion create a new "
+                                + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            // Undo the change to the ColorSpace.
+            mColorSpace = oldColorSpace;
+            nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance());
+            throw e;
+        }
     }
 
     /**
@@ -2197,7 +2187,6 @@
     private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
-    private static native boolean nativeIsConfigF16(long nativeBitmap);
 
     private static native int nativeGetPixel(long nativeBitmap, int x, int y);
     private static native long nativeGetColor(long nativeBitmap, int x, int y);
@@ -2241,11 +2230,10 @@
     private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
                                                                 long nativeColorSpace);
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
-    private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
+    private static native ColorSpace nativeComputeColorSpace(long nativePtr);
     private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
     private static native boolean nativeIsSRGB(long nativePtr);
     private static native boolean nativeIsSRGBLinear(long nativePtr);
-    private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);
 
     private static native void nativeSetImmutable(long nativePtr);
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 7aff041..49c3a3b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -151,12 +151,9 @@
          * 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>
+         * {@link Bitmap.Config#ARGB_8888} configuration and
+         * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
+         * {@link Bitmap.Config#RGBA_F16}).</p>
          *
          * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
          * currently supported. An <code>IllegalArgumentException</code> will
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index c9e4694..0d52338 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1475,7 +1475,7 @@
                 x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 -0.799f, 2.399f,
-                null, // FIXME: Use SRGB_TRANSFER_PARAMETERS
+                SRGB_TRANSFER_PARAMETERS,
                 Named.EXTENDED_SRGB.ordinal()
         );
         sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7ec76d7..b020556 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.os.IBinder;
@@ -28,13 +27,16 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.TimeUtils;
 import android.view.FrameMetricsObserver;
 import android.view.IGraphicsStats;
 import android.view.IGraphicsStatsCallback;
 import android.view.NativeVectorDrawableAnimator;
+import android.view.PixelCopy;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.TextureLayer;
+import android.view.animation.AnimationUtils;
 
 import com.android.internal.util.VirtualRefBasePtr;
 
@@ -42,6 +44,7 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 import sun.misc.Cleaner;
 
@@ -50,13 +53,8 @@
  * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many
  * HardwareRenderer instances as desired.</p>
  *
- * <h3>Threading</h3>
- * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be
- * created & used from a single thread. It does not matter what thread is used, however
- * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same
- * thread, although they can.</p>
- *
  * <h3>Resources & lifecycle</h3>
+ *
  * <p>All HardwareRenderer instances share a common render thread. The render thread contains
  * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first
  * HardwareRenderer created comes with the cost of also creating the associated GPU contexts,
@@ -64,6 +62,7 @@
  * is to have a HardwareRenderer instance for every active {@link Surface}. For example
  * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both
  * of which may be drawing at the same time.</p>
+ *
  * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
  * any {@link Surface} used must have a prompt, reliable consuming side. System-provided
  * consumers such as {@link android.view.SurfaceView},
@@ -73,8 +72,6 @@
  * it is the app's responsibility to ensure that they consume updates promptly and rapidly.
  * Failure to do so will cause the render thread to stall on that surface, blocking all
  * HardwareRenderer instances.</p>
- *
- * @hide
  */
 public class HardwareRenderer {
     private static final String LOG_TAG = "HardwareRenderer";
@@ -89,18 +86,18 @@
      * The renderer is requesting a redraw. This can occur if there's an animation that's running
      * in the RenderNode tree and the hardware renderer is unable to self-animate.
      *
-     * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame
+     * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw
      * will be called again on the next vsync signal.
      */
     public static final int SYNC_REDRAW_REQUESTED = 1 << 0;
 
     /**
      * The hardware renderer no longer has a valid {@link android.view.Surface} to render to.
-     * This can happen if {@link Surface#destroy()} was called. The user should no longer
-     * attempt to call syncAndDrawFrame until a new surface has been provided by calling
+     * This can happen if {@link Surface#release()} was called. The user should no longer
+     * attempt to call syncAndDraw until a new surface has been provided by calling
      * setSurface.
      *
-     * Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
+     * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
      */
     public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
 
@@ -119,6 +116,7 @@
      */
     public static final int SYNC_FRAME_DROPPED = 1 << 3;
 
+    /** @hide */
     @IntDef(value = {
             SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND,
             SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED})
@@ -153,7 +151,6 @@
     protected RenderNode mRootNode;
     private boolean mOpaque = true;
     private boolean mForceDark = false;
-    private FrameInfo mScratchInfo;
     private boolean mIsWideGamut = false;
 
     /**
@@ -175,14 +172,14 @@
      * Destroys the rendering context of this HardwareRenderer. This destroys the resources
      * associated with this renderer and releases the currently set {@link Surface}.
      *
-     * The renderer may be restored from this state by setting a new {@link Surface}, setting
+     * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting
      * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming
-     * rendering with {@link #syncAndDrawFrame(long)}.
+     * rendering by issuing a new {@link FrameRenderRequest}.
      *
-     * It is suggested to call this in response to callbacks such as
+     * <p>It is suggested to call this in response to callbacks such as
      * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}.
      *
-     * Note that if there are any outstanding frame commit callbacks they may end up never being
+     * <p>Note that if there are any outstanding frame commit callbacks they may never being
      * invoked if the frame was deferred to a later vsync.
      */
     public void destroy() {
@@ -204,14 +201,14 @@
      * Sets the center of the light source. The light source point controls the directionality
      * and shape of shadows rendered by RenderNode Z & elevation.
      *
-     * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
+     * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
      * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp.
      *
-     * The light source should be setup both as part of initial configuration, and whenever
+     * <p>The light source should be setup both as part of initial configuration, and whenever
      * the window moves to ensure the light source stays anchored in display space instead
      * of in window space.
      *
-     * This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
+     * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
      * before shadows will work.
      *
      * @param lightX      The X position of the light source
@@ -233,10 +230,10 @@
      * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow
      * has max alpha, and ramps down from the values provided to zero.
      *
-     * These values are typically provided by the current theme, see
+     * <p>These values are typically provided by the current theme, see
      * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
      *
-     * This must be set at least once along with
+     * <p>This must be set at least once along with
      * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work.
      *
      * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default
@@ -254,8 +251,8 @@
     /**
      * Sets the content root to render. It is not necessary to call this whenever the content
      * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's
-     * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)}
-     * is called.
+     * contained within the content node, will be applied whenever a new {@link FrameRenderRequest}
+     * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}.
      *
      * @param content The content to set as the root RenderNode. If null the content root is removed
      *                and the renderer will draw nothing.
@@ -295,6 +292,126 @@
     }
 
     /**
+     * Sets the parameters that can be used to control a render request for a
+     * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
+     * than a single frame request.
+     */
+    public final class FrameRenderRequest {
+        private FrameInfo mFrameInfo = new FrameInfo();
+        private boolean mWaitForPresent;
+
+        private FrameRenderRequest() { }
+
+        private void reset() {
+            mWaitForPresent = false;
+            // Default to the animation time which, if choreographer is in play, will default to the
+            // current vsync time. Otherwise it will be 'now'.
+            mRenderRequest.setVsyncTime(
+                    AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS);
+        }
+
+        /** @hide */
+        public void setFrameInfo(FrameInfo info) {
+            System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length);
+        }
+
+        /**
+         * Sets the vsync time that represents the start point of this frame. Typically this
+         * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time
+         * sources include {@link System#nanoTime()}, however if the result is being displayed
+         * on-screen then using {@link android.view.Choreographer} is strongly recommended to
+         * ensure smooth animations.
+         *
+         * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven
+         * directly by RenderThread will not be synchronized properly with the current frame.
+         *
+         * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds
+         *                  and should come from a CLOCK_MONOTONIC source.
+         *
+         * @return this instance
+         */
+        public FrameRenderRequest setVsyncTime(long vsyncTime) {
+            mFrameInfo.setVsync(vsyncTime, vsyncTime);
+            mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
+            return this;
+        }
+
+        /**
+         * Adds a frame commit callback. This callback will be invoked when the current rendering
+         * content has been rendered into a frame and submitted to the swap chain. The frame may
+         * not currently be visible on the display when this is invoked, but it has been submitted.
+         * This callback is useful in combination with {@link PixelCopy} to capture the current
+         * rendered content of the UI reliably.
+         *
+         * @param executor The executor to run the callback on. It is strongly recommended that
+         *                 this executor post to a different thread, as the calling thread is
+         *                 highly sensitive to being blocked.
+         * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
+         *                            Will be invoked on the given {@link Executor}.
+         *
+         * @return this instance
+         */
+        public FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
+                @NonNull Runnable frameCommitCallback) {
+            setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+            return this;
+        }
+
+        /**
+         * Sets whether or not {@link #syncAndDraw()} should block until the frame has been
+         * presented. If this is true and {@link #syncAndDraw()} does not return
+         * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned
+         * the frame has been submitted to the {@link Surface}. The default and typically
+         * recommended value is false, as blocking for present will prevent pipelining from
+         * happening, reducing overall throughput. This is useful for situations such as
+         * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired
+         * to block until a frame has been presented to ensure first-frame consistency with
+         * other Surfaces.
+         *
+         * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until
+         *                   completion.
+         * @return this instance
+         */
+        public FrameRenderRequest setWaitForPresent(boolean shouldWait) {
+            mWaitForPresent = shouldWait;
+            return this;
+        }
+
+        /**
+         * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This
+         * {@link FrameRenderRequest} instance should no longer be used after calling this method.
+         * The system internally may reuse instances of {@link FrameRenderRequest} to reduce
+         * allocation churn.
+         *
+         * @return The result of the sync operation. See {@link SyncAndDrawResult}.
+         */
+        @SyncAndDrawResult
+        public int syncAndDraw() {
+            int syncResult = syncAndDrawFrame(mFrameInfo);
+            if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) {
+                fence();
+            }
+            return syncResult;
+        }
+    }
+
+    private FrameRenderRequest mRenderRequest = new FrameRenderRequest();
+
+    /**
+     * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used
+     * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with
+     * the RenderThread and then renders a single frame to the Surface set with
+     * {@link #setSurface(Surface)}.
+     *
+     * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every
+     * frame, so the caller should not hold onto it for longer than a single render request.
+     */
+    public FrameRenderRequest createRenderRequest() {
+        mRenderRequest.reset();
+        return mRenderRequest;
+    }
+
+    /**
      * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
      *
      * @hide
@@ -305,54 +422,15 @@
     }
 
     /**
-     * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
-     *
-     * @param vsyncTime The vsync timestamp for this frame. Typically this comes from
-     *                  {@link android.view.Choreographer.FrameCallback}. Must be set and be valid
-     *                  as the renderer uses this time internally to drive animations.
-     * @return The result of the sync operation. See {@link SyncAndDrawResult}.
-     */
-    @SyncAndDrawResult
-    public int syncAndDrawFrame(long vsyncTime) {
-        if (mScratchInfo == null) {
-            mScratchInfo = new FrameInfo();
-        }
-        mScratchInfo.setVsync(vsyncTime, vsyncTime);
-        mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
-        return syncAndDrawFrame(mScratchInfo);
-    }
-
-    /**
-     * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
-     * frameCommitCallback callback will be invoked when the current rendering content has been
-     * rendered into a frame and submitted to the swap chain.
-     *
-     * @param vsyncTime           The vsync timestamp for this frame. Typically this comes from
-     *                            {@link android.view.Choreographer.FrameCallback}. Must be set and
-     *                            be valid as the renderer uses this time internally to drive
-     *                            animations.
-     * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
-     *                            Will be invoked on the current {@link android.os.Looper} thread.
-     * @return The result of the sync operation. See {@link SyncAndDrawResult}.
-     */
-    @SyncAndDrawResult
-    public int syncAndDrawFrame(long vsyncTime,
-            @Nullable Runnable frameCommitCallback) {
-        if (frameCommitCallback != null) {
-            setFrameCompleteCallback(frameNr -> frameCommitCallback.run());
-        }
-        return syncAndDrawFrame(vsyncTime);
-    }
-
-    /**
      * Suspends any current rendering into the surface but do not do any destruction. This
      * is useful to temporarily suspend using the active Surface in order to do any Surface
      * mutations necessary.
      *
-     * Any subsequent draws will override the pause, resuming normal operation.
+     * <p>Any subsequent draws will override the pause, resuming normal operation.
      *
      * @return true if there was an outstanding render request, false otherwise. If this is true
-     * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest
+     * the caller should ensure that {@link #createRenderRequest()}
+     * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest
      * possible time to resume normal operation.
      *
      * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old
@@ -367,14 +445,14 @@
 
     /**
      * Hard stops rendering into the surface. If the renderer is stopped it will
-     * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still
-     * sync over the latest rendering content, however they will not render and instead
+     * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
+     * still sync over the latest rendering content, however they will not render and instead
      * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
      *
-     * If false is passed then rendering will resume as normal. Any pending rendering requests
+     * <p>If false is passed then rendering will resume as normal. Any pending rendering requests
      * will produce a new frame at the next vsync signal.
      *
-     * This is useful in combination with lifecycle events such as {@link Activity#onStop()}
+     * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}
      * and {@link Activity#onStart()}.
      *
      * @param stopped true to stop all rendering, false to resume
@@ -384,24 +462,26 @@
     }
 
     /**
-     * Destroys all hardware rendering resources associated with the current rendering content.
+     * Destroys all the display lists associated with the current rendering content.
      * This includes releasing a reference to the current content root RenderNode. It will
      * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume
-     * rendering after calling this.
+     * rendering after calling this, along with re-recording the display lists for the
+     * RenderNode tree.
      *
-     * It is recommended, but not necessary, to use this in combination with lifecycle events
+     * <p>It is recommended, but not necessary, to use this in combination with lifecycle events
      * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to
      * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as
      * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN}
      *
      * See also {@link #setStopped(boolean)}
      */
-    public void destroyHardwareResources() {
+    public void clearContent() {
         nDestroyHardwareResources(mNativeProxy);
     }
 
     /**
      * Whether or not the force-dark feature should be used for this renderer.
+     * @hide
      */
     public boolean setForceDark(boolean enable) {
         if (mForceDark != enable) {
@@ -415,20 +495,24 @@
     /**
      * Allocate buffers ahead of time to avoid allocation delays during rendering.
      *
-     * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
+     * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
      * memory usage of Surfaces that render rarely or never hit triple buffering. However
      * for UI it can result in a slight bit of jank on first launch. This hint will
      * tell the HardwareRenderer that now is a good time to allocate the 3 buffers
      * necessary for typical rendering.
      *
-     * Must be called after a {@link Surface} has been set.
+     * <p>Must be called after a {@link Surface} has been set.
+     *
+     * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response
+     * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public
+     * @hide
      */
     public void allocateBuffers() {
         nAllocateBuffers(mNativeProxy);
     }
 
     /**
-     * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will
+     * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will
      * be coming soon. This is used to help schedule when RenderThread-driven animations will
      * happen as the renderer wants to avoid producing more than one frame per vsync signal.
      */
@@ -439,7 +523,7 @@
     /**
      * Change the HardwareRenderer's opacity. Will take effect on the next frame produced.
      *
-     * If the renderer is set to opaque it is the app's responsibility to ensure that the
+     * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the
      * content renders to every pixel of the Surface, otherwise corruption may result. Note that
      * this includes ensuring that the first draw of any given pixel does not attempt to blend
      * against the destination. If this is false then the hardware renderer will clear to
@@ -527,7 +611,7 @@
     }
 
     /**
-     * Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called.
+     * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called.
      * This is a signal that the contents of the RenderNode tree are no longer safe to play back.
      * In practice this usually means that there are Functor pointers in the
      * display list that are no longer valid.
@@ -718,10 +802,8 @@
      * Interface for listening to picture captures
      * @hide
      */
-    @TestApi
     public interface PictureCapturedCallback {
         /** @hide */
-        @TestApi
         void onPictureCaptured(Picture picture);
     }
 
@@ -945,6 +1027,18 @@
      */
     public static native void disableVsync();
 
+    /**
+     * Start render thread and initialize EGL or Vulkan.
+     *
+     * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
+     * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
+     * its first frame adds directly to user-visible app launch latency.
+     *
+     * Should only be called after GraphicsEnvironment.chooseDriver().
+     * @hide
+     */
+    public static native void preload();
+
     /** @hide */
     protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
 
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 466a5fc..9b5e330 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -372,7 +372,7 @@
             }
             mResources = res;
             mInputStream = is;
-            mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+            mInputDensity = inputDensity;
         }
 
         final Resources mResources;
@@ -1556,12 +1556,9 @@
      * decoder will pick either the color space embedded in the image or the
      * {@link ColorSpace} 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>
+     * {@link Bitmap.Config#ARGB_8888} configuration and
+     * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
+     * {@link Bitmap.Config#RGBA_F16}).</p>
      *
      * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
      * currently supported. An <code>IllegalArgumentException</code> will
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 56be05b..e93e757 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -127,7 +127,11 @@
     static Typeface sDefaultTypeface;
 
     // Following two fields are not used but left for hiddenapi private list
-    @UnsupportedAppUsage
+    /**
+     * sSystemFontMap is read only and unmodifiable.
+     * Use public API {@link #create(String, int)} to get the typeface for given familyName.
+     */
+    @UnsupportedAppUsage(trackingBug = 123769347)
     static final Map<String, Typeface> sSystemFontMap;
 
     // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 789e38c..d7aee77 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -43,6 +43,7 @@
 import android.graphics.Rect;
 import android.graphics.RenderNode;
 import android.os.Build;
+import android.os.Handler;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.IntArray;
@@ -1241,6 +1242,7 @@
 
         // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
         private static final int MAX_SAMPLE_POINTS = 300;
+        private Handler mHandler;
         private AnimatorListener mListener = null;
         private final LongArray mStartDelays = new LongArray();
         private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1671,6 +1673,9 @@
                                 .mRootName);
             }
             mStarted = true;
+            if (mHandler == null) {
+                mHandler = new Handler();
+            }
             nStart(mSetPtr, this, ++mLastListenerId);
             invalidateOwningView();
             if (mListener != null) {
@@ -1780,7 +1785,7 @@
         // onFinished: should be called from native
         @UnsupportedAppUsage
         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
-            set.onAnimationEnd(id);
+            set.mHandler.post(() -> set.onAnimationEnd(id));
         }
 
         private void transferPendingActions(VectorDrawableAnimator animatorSet) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index aa29174..3dc884e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -17,7 +17,6 @@
 package android.security.keystore;
 
 import android.security.Credentials;
-import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
@@ -204,7 +203,12 @@
                         }
                     }
                 }
-
+                if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+                    if (mKeySizeBits != 168) {
+                        throw new InvalidAlgorithmParameterException(
+                            "3DES key size must be 168 bits.");
+                    }
+                }
                 if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
                     if (mKeySizeBits < 64) {
                         throw new InvalidAlgorithmParameterException(
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 742813e..875b90b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1647,6 +1647,10 @@
     // The overlay must reside of the product partition or must have existed on the product
     // partition before an upgrade to overlay these resources.
     POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must be signed with the same signature as the actor of the target resource,
+    // which can be separate or the same as the target package with the resource.
+    POLICY_SIGNATURE = 0x00000010,
   };
   uint32_t policy_flags;
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0335a7c..793dd8d 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -318,6 +318,7 @@
         "tests/unit/RenderNodeDrawableTests.cpp",
         "tests/unit/RenderNodeTests.cpp",
         "tests/unit/RenderPropertiesTests.cpp",
+        "tests/unit/RenderThreadTests.cpp",
         "tests/unit/ShaderCacheTests.cpp",
         "tests/unit/SkiaBehaviorTests.cpp",
         "tests/unit/SkiaDisplayListTests.cpp",
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index 237fc62..b93f078 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -23,7 +23,7 @@
 
 class FrameMetricsObserver : public VirtualLightRefBase {
 public:
-    virtual void notify(const int64_t* buffer);
+    virtual void notify(const int64_t* buffer) = 0;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 76c5660..2ffda83 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -107,7 +107,7 @@
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
     } else {
-        mRenderThread.vulkanManager().initialize();
+        mRenderThread.requireVkContext();
     }
     if (!image.get()) {
         return CopyResult::UnknownError;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 15f53f2..87cffb5 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -120,7 +120,7 @@
 }
 
 DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
-    mVkManager.initialize();
+    mRenderThread.requireVkContext();
 
     return new DeferredLayerUpdater(mRenderThread.renderState());
 }
@@ -136,8 +136,9 @@
 
     setSurfaceColorProperties(colorMode);
     if (surface) {
+        mRenderThread.requireVkContext();
         mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace,
-                                              mSurfaceColorType);
+                                              mSurfaceColorType, mRenderThread.getGrContext());
     }
 
     return mVkSurface != nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 720c603..34f76d9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -381,6 +381,14 @@
     });
 }
 
+void RenderProxy::preload() {
+    // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver.
+    auto& thread = RenderThread::getInstance();
+    thread.queue().post([&thread]() {
+        thread.preload();
+    });
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6e1bfd7..a1a5551 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -131,6 +131,8 @@
 
     ANDROID_API static void disableVsync();
 
+    ANDROID_API static void preload();
+
     static void repackVectorDrawableAtlas();
 
     static void releaseVDAtlasEntries();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3904ed2..08edd20 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -41,6 +41,7 @@
 #include <utils/Condition.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
+#include <thread>
 
 namespace android {
 namespace uirenderer {
@@ -173,11 +174,8 @@
     initializeDisplayEventReceiver();
     mEglManager = new EglManager();
     mRenderState = new RenderState(*this);
-    mVkManager = new VulkanManager(*this);
+    mVkManager = new VulkanManager();
     mCacheManager = new CacheManager(mDisplayInfo);
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        mVkManager->initialize();
-    }
 }
 
 void RenderThread::requireGlContext() {
@@ -195,8 +193,7 @@
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
-    options.fPreferExternalImagesOverES3 = true;
-    options.fDisableDistanceFieldPaths = true;
+    initGrContextOptions(options);
     auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
     auto size = glesVersion ? strlen(glesVersion) : -1;
     cacheManager().configureContext(&options, glesVersion, size);
@@ -205,6 +202,25 @@
     setGrContext(grContext);
 }
 
+void RenderThread::requireVkContext() {
+    if (mVkManager->hasVkContext()) {
+        return;
+    }
+    mVkManager->initialize();
+    GrContextOptions options;
+    initGrContextOptions(options);
+    // TODO: get a string describing the SPIR-V compiler version and use it here
+    cacheManager().configureContext(&options, nullptr, 0);
+    sk_sp<GrContext> grContext = mVkManager->createContext(options);
+    LOG_ALWAYS_FATAL_IF(!grContext.get());
+    setGrContext(grContext);
+}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {
+    options.fPreferExternalImagesOverES3 = true;
+    options.fDisableDistanceFieldPaths = true;
+}
+
 void RenderThread::destroyRenderingContext() {
     mFunctorManager.onContextDestroyed();
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
@@ -328,6 +344,7 @@
 
 bool RenderThread::threadLoop() {
     setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
+    Looper::setForThread(mLooper);
     if (gOnStartHook) {
         gOnStartHook("RenderThread");
     }
@@ -390,6 +407,17 @@
     return gettid() == getInstance().getTid();
 }
 
+void RenderThread::preload() {
+    std::thread eglInitThread([]() {
+        //TODO: don't load EGL drivers for Vulkan, when HW bitmap uploader is refactored.
+        eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    });
+    eglInitThread.detach();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        requireVkContext();
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index b182928..329b4b9 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -112,8 +112,11 @@
     void dumpGraphicsMemory(int fd);
 
     void requireGlContext();
+    void requireVkContext();
     void destroyRenderingContext();
 
+    void preload();
+
     /**
      * isCurrent provides a way to query, if the caller is running on
      * the render thread.
@@ -122,6 +125,8 @@
      */
     static bool isCurrent();
 
+    static void initGrContextOptions(GrContextOptions& options);
+
 protected:
     virtual bool threadLoop() override;
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 90397fd..3b43f12 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -55,11 +55,7 @@
 #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
 #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
 
-VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
-
 void VulkanManager::destroy() {
-    mRenderThread.setGrContext(nullptr);
-
     // We don't need to explicitly free the command buffer since it automatically gets freed when we
     // delete the VkCommandPool below.
     mDummyCB = VK_NULL_HANDLE;
@@ -333,29 +329,10 @@
     LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
     LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
 
-    GrVkExtensions extensions;
-    this->setupDevice(extensions, mPhysicalDeviceFeatures2);
+    this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
-    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
-        if (device != VK_NULL_HANDLE) {
-            return vkGetDeviceProcAddr(device, proc_name);
-        }
-        return vkGetInstanceProcAddr(instance, proc_name);
-    };
-
-    GrVkBackendContext backendContext;
-    backendContext.fInstance = mInstance;
-    backendContext.fPhysicalDevice = mPhysicalDevice;
-    backendContext.fDevice = mDevice;
-    backendContext.fQueue = mGraphicsQueue;
-    backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fMaxAPIVersion = mAPIVersion;
-    backendContext.fVkExtensions = &extensions;
-    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
-    backendContext.fGetProc = std::move(getProc);
-
     // create the command pool for the command buffers
     if (VK_NULL_HANDLE == mCommandPool) {
         VkCommandPoolCreateInfo commandPoolInfo;
@@ -376,22 +353,35 @@
     }
     LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
 
-
     mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
 
-    GrContextOptions options;
-    options.fDisableDistanceFieldPaths = true;
-    // TODO: get a string describing the SPIR-V compiler version and use it here
-    mRenderThread.cacheManager().configureContext(&options, nullptr, 0);
-    sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
-    LOG_ALWAYS_FATAL_IF(!grContext.get());
-    mRenderThread.setGrContext(grContext);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
 }
 
+sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) {
+    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+
+    GrVkBackendContext backendContext;
+    backendContext.fInstance = mInstance;
+    backendContext.fPhysicalDevice = mPhysicalDevice;
+    backendContext.fDevice = mDevice;
+    backendContext.fQueue = mGraphicsQueue;
+    backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+    backendContext.fMaxAPIVersion = mAPIVersion;
+    backendContext.fVkExtensions = &mExtensions;
+    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
+    backendContext.fGetProc = std::move(getProc);
+
+    return GrContext::MakeVulkan(backendContext, options);
+}
+
 VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
     return VkFunctorInitParams{
             .instance = mInstance,
@@ -470,8 +460,9 @@
         ColorMode colorMode = surface->mColorMode;
         sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
         SkColorType colorType = surface->mColorType;
+        GrContext* grContext = surface->mGrContext;
         destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType);
+        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext);
         surface = *surfaceOut;
         if (!surface) {
             return nullptr;
@@ -650,7 +641,7 @@
 
         VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
         imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
-                mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
+                surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin,
                 surface->mColorType, surface->mColorSpace, &props);
     }
 
@@ -880,15 +871,15 @@
 
 VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
                                             sk_sp<SkColorSpace> surfaceColorSpace,
-                                            SkColorType surfaceColorType) {
-    initialize();
-
+                                            SkColorType surfaceColorType,
+                                            GrContext* grContext) {
+    LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized");
     if (!window) {
         return nullptr;
     }
 
     VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
-                                               surfaceColorType);
+                                               surfaceColorType, grContext);
 
     VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
     memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 1fe6c65..95c9630 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -22,6 +22,8 @@
 #endif
 #include <vulkan/vulkan.h>
 
+#include <GrContextOptions.h>
+#include <vk/GrVkExtensions.h>
 #include <SkSurface.h>
 #include <ui/Fence.h>
 #include <utils/StrongPointer.h>
@@ -39,9 +41,9 @@
 class VulkanSurface {
 public:
     VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
-                  SkColorType colorType)
+                  SkColorType colorType, GrContext* grContext)
             : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
-              mColorType(colorType) {}
+              mColorType(colorType), mGrContext(grContext) {}
 
     sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
 
@@ -93,6 +95,7 @@
     SkColorType mColorType;
     VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     SkMatrix mPreTransform;
+    GrContext* mGrContext;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -100,6 +103,9 @@
 // windowing contexts. The VulkanManager must be initialized before use.
 class VulkanManager {
 public:
+    explicit VulkanManager() {}
+    ~VulkanManager() { destroy(); }
+
     // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must
     // be call once before use of the VulkanManager. Multiple calls after the first will simiply
     // return.
@@ -112,7 +118,8 @@
     // VulkanSurface object which is returned.
     VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
                                  sk_sp<SkColorSpace> surfaceColorSpace,
-                                 SkColorType surfaceColorType);
+                                 SkColorType surfaceColorType,
+                                 GrContext* grContext);
 
     // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
@@ -143,12 +150,9 @@
     // Returned pointers are owned by VulkanManager.
     VkFunctorInitParams getVkFunctorInitParams() const;
 
+    sk_sp<GrContext> createContext(const GrContextOptions& options);
+
 private:
-    friend class RenderThread;
-
-    explicit VulkanManager(RenderThread& thread);
-    ~VulkanManager() { destroy(); }
-
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
     // VkPhysicalDeviceFeatures struct.
     void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
@@ -231,8 +235,6 @@
     VkPtr<PFN_vkWaitForFences> mWaitForFences;
     VkPtr<PFN_vkResetFences> mResetFences;
 
-    RenderThread& mRenderThread;
-
     VkInstance mInstance = VK_NULL_HANDLE;
     VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
     VkDevice mDevice = VK_NULL_HANDLE;
@@ -256,6 +258,7 @@
         BufferAge,
     };
     SwapBehavior mSwapBehavior = SwapBehavior::Discard;
+    GrVkExtensions mExtensions;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a9f651d..e8ba15f 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -100,7 +100,7 @@
     // RenderState only valid once RenderThread is running, so queried here
     renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        renderThread.vulkanManager().initialize();
+        renderThread.requireVkContext();
     } else {
         renderThread.requireGlContext();
     }
diff --git a/libs/hwui/tests/unit/RenderThreadTests.cpp b/libs/hwui/tests/unit/RenderThreadTests.cpp
new file mode 100644
index 0000000..af8ae78
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderThreadTests.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "tests/common/TestUtils.h"
+#include <utils/Looper.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+RENDERTHREAD_TEST(RenderThread, isLooper) {
+    ASSERT_TRUE(Looper::getForThread() != nullptr);
+}
+
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8cdcc46..0289d3f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -68,10 +68,12 @@
     void processQueue() { mQueue.process(); }
 
     virtual bool threadLoop() override {
+        Looper::setForThread(mLooper);
         while (!exitPending()) {
             waitForWork();
             processQueue();
         }
+        Looper::setForThread(nullptr);
         return false;
     }
 
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
index a84dc48..d463f87 100644
--- a/libs/incident/proto/android/os/header.proto
+++ b/libs/incident/proto/android/os/header.proto
@@ -37,4 +37,9 @@
       optional int64 id = 2; // The unique id of the statsd config.
     }
     optional StatsdConfigKey config_key = 3;
+
+    // Details about the trigger. com.android.os.AlertTriggerDetails
+    // Only use bytes type here to avoid indirect dependency on atoms.proto
+    // And this header passes through incidentd without incidentd parsing it.
+    optional bytes trigger_details = 4;
 }
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index d742cc3..733b866 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -115,10 +115,14 @@
 
     mLocked.pointerSprite.clear();
 
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        delete mLocked.spots.itemAt(i);
+    for (auto& it : mLocked.spotsByDisplay) {
+        const std::vector<Spot*>& spots = it.second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots; i++) {
+            delete spots[i];
+        }
     }
-    mLocked.spots.clear();
+    mLocked.spotsByDisplay.clear();
     mLocked.recycledSprites.clear();
 }
 
@@ -271,22 +275,30 @@
 }
 
 void PointerController::setSpots(const PointerCoords* spotCoords,
-        const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+        const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
 #if DEBUG_POINTER_UPDATES
     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
         uint32_t id = idBits.firstMarkedBit();
         idBits.clearBit(id);
         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
                 c.getAxisValue(AMOTION_EVENT_AXIS_X),
                 c.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                displayId);
     }
 #endif
 
     AutoMutex _l(mLock);
 
+    std::vector<Spot*> newSpots;
+    std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
+            mLocked.spotsByDisplay.find(displayId);
+    if (iter != mLocked.spotsByDisplay.end()) {
+        newSpots = iter->second;
+    }
+
     mSpriteController->openTransaction();
 
     // Add or move spots for fingers that are down.
@@ -298,17 +310,17 @@
         float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
         float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
 
-        Spot* spot = getSpotLocked(id);
+        Spot* spot = getSpot(id, newSpots);
         if (!spot) {
-            spot = createAndAddSpotLocked(id);
+            spot = createAndAddSpotLocked(id, newSpots);
         }
 
-        spot->updateSprite(&icon, x, y);
+        spot->updateSprite(&icon, x, y, displayId);
     }
 
     // Remove spots for fingers that went up.
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+    for (size_t i = 0; i < newSpots.size(); i++) {
+        Spot* spot = newSpots[i];
         if (spot->id != Spot::INVALID_ID
                 && !spotIdBits.hasBit(spot->id)) {
             fadeOutAndReleaseSpotLocked(spot);
@@ -316,6 +328,7 @@
     }
 
     mSpriteController->closeTransaction();
+    mLocked.spotsByDisplay[displayId] = newSpots;
 }
 
 void PointerController::clearSpots() {
@@ -539,21 +552,33 @@
     }
 
     // Animate spots that are fading out and being removed.
-    for (size_t i = 0; i < mLocked.spots.size();) {
-        Spot* spot = mLocked.spots.itemAt(i);
-        if (spot->id == Spot::INVALID_ID) {
-            spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
-            if (spot->alpha <= 0) {
-                mLocked.spots.removeAt(i);
-                releaseSpotLocked(spot);
-                continue;
-            } else {
-                spot->sprite->setAlpha(spot->alpha);
-                keepAnimating = true;
+    for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) {
+        std::vector<Spot*>& spots = it->second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots;) {
+            Spot* spot = spots[i];
+            if (spot->id == Spot::INVALID_ID) {
+                spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+                if (spot->alpha <= 0) {
+                    spots.erase(spots.begin() + i);
+                    releaseSpotLocked(spot);
+                    numSpots--;
+                    continue;
+                } else {
+                    spot->sprite->setAlpha(spot->alpha);
+                    keepAnimating = true;
+                }
             }
+            ++i;
         }
-        ++i;
+
+        if (spots.size() == 0) {
+            it = mLocked.spotsByDisplay.erase(it);
+        } else {
+            ++it;
+        }
     }
+
     return keepAnimating;
 }
 
@@ -655,47 +680,49 @@
     mSpriteController->closeTransaction();
 }
 
-PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
         if (spot->id == id) {
             return spot;
         }
     }
-    return NULL;
+
+    return nullptr;
 }
 
-PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
+PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id,
+        std::vector<Spot*>& spots) {
     // Remove spots until we have fewer than MAX_SPOTS remaining.
-    while (mLocked.spots.size() >= MAX_SPOTS) {
-        Spot* spot = removeFirstFadingSpotLocked();
+    while (spots.size() >= MAX_SPOTS) {
+        Spot* spot = removeFirstFadingSpotLocked(spots);
         if (!spot) {
-            spot = mLocked.spots.itemAt(0);
-            mLocked.spots.removeAt(0);
+            spot = spots[0];
+            spots.erase(spots.begin());
         }
         releaseSpotLocked(spot);
     }
 
     // Obtain a sprite from the recycled pool.
     sp<Sprite> sprite;
-    if (! mLocked.recycledSprites.isEmpty()) {
-        sprite = mLocked.recycledSprites.top();
-        mLocked.recycledSprites.pop();
+    if (! mLocked.recycledSprites.empty()) {
+        sprite = mLocked.recycledSprites.back();
+        mLocked.recycledSprites.pop_back();
     } else {
         sprite = mSpriteController->createSprite();
     }
 
     // Return the new spot.
     Spot* spot = new Spot(id, sprite);
-    mLocked.spots.push(spot);
+    spots.push_back(spot);
     return spot;
 }
 
-PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
         if (spot->id == Spot::INVALID_ID) {
-            mLocked.spots.removeAt(i);
+            spots.erase(spots.begin() + i);
             return spot;
         }
     }
@@ -706,7 +733,7 @@
     spot->sprite->clearIcon();
 
     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
-        mLocked.recycledSprites.push(spot->sprite);
+        mLocked.recycledSprites.push_back(spot->sprite);
     }
 
     delete spot;
@@ -720,9 +747,13 @@
 }
 
 void PointerController::fadeOutAndReleaseAllSpotsLocked() {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
-        fadeOutAndReleaseSpotLocked(spot);
+    for (auto& it : mLocked.spotsByDisplay) {
+        const std::vector<Spot*>& spots = it.second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots; i++) {
+            Spot* spot = spots[i];
+            fadeOutAndReleaseSpotLocked(spot);
+        }
     }
 }
 
@@ -743,12 +774,13 @@
 
 // --- PointerController::Spot ---
 
-void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
+void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+        int32_t displayId) {
     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
     sprite->setAlpha(alpha);
     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
     sprite->setPosition(x, y);
-
+    sprite->setDisplayId(displayId);
     this->x = x;
     this->y = y;
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index be05786..52305b8 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -103,7 +103,7 @@
 
     virtual void setPresentation(Presentation presentation);
     virtual void setSpots(const PointerCoords* spotCoords,
-            const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId);
     virtual void clearSpots();
 
     void updatePointerIcon(int32_t iconId);
@@ -133,7 +133,7 @@
                 : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
                   x(0.0f), y(0.0f), lastIcon(NULL) { }
 
-        void updateSprite(const SpriteIcon* icon, float x, float y);
+        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
 
     private:
         const SpriteIcon* lastIcon;
@@ -180,8 +180,8 @@
 
         int32_t buttonState;
 
-        Vector<Spot*> spots;
-        Vector<sp<Sprite> > recycledSprites;
+        std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
+        std::vector<sp<Sprite> > recycledSprites;
     } mLocked GUARDED_BY(mLock);
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
@@ -200,9 +200,9 @@
     void removeInactivityTimeoutLocked();
     void updatePointerLocked();
 
-    Spot* getSpotLocked(uint32_t id);
-    Spot* createAndAddSpotLocked(uint32_t id);
-    Spot* removeFirstFadingSpotLocked();
+    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
+    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
+    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
     void releaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 59eff64..a545f2e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -96,7 +96,7 @@
             STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
             STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
             STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
-            STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN
+            STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN, STATE_2ND_CODE_LOCK
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
@@ -144,6 +144,9 @@
      */
     public static final int STATE_GLO_TOD_KNOWN = (1<<15);
 
+    /** This GNSS measurement's tracking state has secondary code lock. */
+    public static final int STATE_2ND_CODE_LOCK  = (1 << 16);
+
     /**
      * All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any
      * individual measurement.)
@@ -517,6 +520,9 @@
         if ((mState & STATE_SBAS_SYNC) != 0) {
             builder.append("SbasSync|");
         }
+        if ((mState & STATE_2ND_CODE_LOCK) != 0) {
+            builder.append("2ndCodeLock|");
+        }
 
         int remainingStates = mState & ~STATE_ALL;
         if (remainingStates > 0) {
@@ -531,96 +537,315 @@
     /**
      * Gets the received GNSS satellite time, at the measurement time, in nanoseconds.
      *
-     * <p>For GPS &amp; QZSS, this is:
-     * <ul>
-     * <li>Received GPS Time-of-Week at the measurement time, in nanoseconds.</li>
-     * <li>The value is relative to the beginning of the current GPS week.</li>
-     * </ul>
+     * <p>The received satellite time is relative to the beginning of the system week for all
+     * constellations except for Glonass where it is relative to the beginning of the Glonass
+     * system day.
      *
-     * <p>Given the highest sync state that can be achieved, per each satellite, valid range
-     * for this field can be:
-     * <pre>
-     *     Searching       : [ 0       ]   : STATE_UNKNOWN
-     *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
-     *     Bit sync        : [ 0  20ms ]   : STATE_BIT_SYNC is set
-     *     Subframe sync   : [ 0    6s ]   : STATE_SUBFRAME_SYNC is set
-     *     TOW decoded     : [ 0 1week ]   : STATE_TOW_DECODED is set
-     *     TOW Known       : [ 0 1week ]   : STATE_TOW_KNOWN set</pre>
+     * <p>The table below indicates the valid range of the received GNSS satellite time. These
+     * ranges depend on the constellation and code being tracked and the state of the tracking
+     * algorithms given by the {@link #getState} method. The minimum value of this field is zero.
+     * The maximum value of this field is determined by looking across all of the state flags
+     * that are set, for the given constellation and code type, and finding the the maximum value
+     * in this table.
      *
-     * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+     * <p>For example, for GPS L1 C/A, if STATE_TOW_KNOWN is set, this field can be any value from 0
+     * to 1 week (in nanoseconds), and for GAL E1B code, if only STATE_GAL_E1BC_CODE_LOCK is set,
+     * then this field can be any value from 0 to 4 milliseconds (in nanoseconds.)
+     *
+     * <table border="1">
+     *   <thead>
+     *     <tr>
+     *       <td />
+     *       <td colspan="3"><strong>GPS/QZSS</strong></td>
+     *       <td><strong>GLNS</strong></td>
+     *       <td colspan="2"><strong>BDS</strong></td>
+     *       <td colspan="3"><strong>GAL</strong></td>
+     *       <td><strong>SBAS</strong></td>
+     *     </tr>
+     *     <tr>
+     *       <td><strong>State Flag</strong></td>
+     *       <td><strong>L1 C/A</strong></td>
+     *       <td><strong>L5I</strong></td>
+     *       <td><strong>L5Q</strong></td>
+     *       <td><strong>L1OF</strong></td>
+     *       <td><strong>B1I (D1)</strong></td>
+     *       <td><strong>B1I &nbsp;(D2)</strong></td>
+     *       <td><strong>E1B</strong></td>
+     *       <td><strong>E1C</strong></td>
+     *       <td><strong>E5AQ</strong></td>
+     *       <td><strong>L1 C/A</strong></td>
+     *     </tr>
+     *   </thead>
+     *   <tbody>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_UNKNOWN</strong>
+     *       </td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_CODE_LOCK</strong>
+     *       </td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SYMBOL_SYNC</strong>
+     *       </td>
+     *       <td>20 ms (optional)</td>
+     *       <td>10 ms</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>10 ms</td>
+     *       <td>20 ms (optional)</td>
+     *       <td>2 ms</td>
+     *       <td>4 ms (optional)</td>
+     *       <td>4 ms (optional)</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>2 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BIT_SYNC</strong>
+     *       </td>
+     *       <td>20 ms</td>
+     *       <td>20 ms</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>20 ms</td>
+     *       <td>20 ms</td>
+     *       <td>-</td>
+     *       <td>8 ms</td>
+     *       <td>-</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>4 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SUBFRAME_SYNC</strong>
+     *       </td>
+     *       <td>6s</td>
+     *       <td>6s</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>6 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_TOW_DECODED</strong>
+     *       </td>
+     *       <td colspan="2">1 week</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td colspan="2">1 week</td>
+     *       <td colspan="2">1 week</td>
+     *       <td>-</td>
+     *       <td>1 week</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_TOW_KNOWN</strong>
+     *       </td>
+     *       <td colspan="3">1 week</td>
+     *       <td>1 day</td>
+     *       <td colspan="2">1 week</td>
+     *       <td colspan="3">1 week</td>
+     *       <td>1 week</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_STRING_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_TOD_DECODED</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_TOD_KNOWN</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BDS_D2_BIT_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>600 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1BC_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>4 ms</td>
+     *       <td>4 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_2ND_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>10 ms (optional)</td>
+     *       <td>20 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms (optional)</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1B_PAGE_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SBAS_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 s</td>
+     *     </tr>
+     *   </tbody>
+     * </table>
+     *
+     * <p>Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
      * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
      *
-     * <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS}
-     * must be set accordingly, in the 'state' field.
+     * <p>Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be
+     * set accordingly, in the 'state' field. This value must be populated, unless the 'state' ==
+     * STATE_UNKNOWN.
      *
-     * <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}.
-     *
-     * <p>For Glonass, this is:
+     * <p>Note on optional flags:
      * <ul>
-     * <li>Received Glonass time of day, at the measurement time in nanoseconds.</li>
+     *     <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+     *     same as the bit length.
+     *     <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+     *     implied by STATE_CODE_LOCK.
+     *     <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+     *     <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+     *     STATE_GAL_E1C_2ND_CODE_LOCK.
+     *     <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+     *     STATE_GAL_E1BC_CODE_LOCK.
      * </ul>
-     *
-     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
-     * this field can be:
-     * <pre>
-     *     Searching           : [ 0       ]   : STATE_UNKNOWN
-     *     C/A code lock       : [ 0   1ms ]   : STATE_CODE_LOCK is set
-     *     Symbol sync         : [ 0  10ms ]   : STATE_SYMBOL_SYNC is set
-     *     Bit sync            : [ 0  20ms ]   : STATE_BIT_SYNC is set
-     *     String sync         : [ 0    2s ]   : STATE_GLO_STRING_SYNC is set
-     *     Time of day decoded : [ 0  1day ]   : STATE_GLO_TOD_DECODED is set
-     *     Time of day known   : [ 0  1day ]   : STATE_GLO_TOD_KNOWN set</pre>
-     *
-     * Note: Time of day known refers to the case where it is possibly not decoded over the air but
-     * has been determined from other sources. If Time of day decoded is set then Time of day known
-     * must also be set.
-     *
-     * <p>For Beidou, this is:
-     * <ul>
-     * <li>Received Beidou time of week, at the measurement time in nanoseconds.</li>
-     * </ul>
-     *
-     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
-     * this field can be:
-     * <pre>
-     *     Searching              : [ 0       ]   : STATE_UNKNOWN
-     *     C/A code lock          : [ 0   1ms ]   : STATE_CODE_LOCK is set
-     *     Bit sync (D2)          : [ 0   2ms ]   : STATE_BDS_D2_BIT_SYNC is set
-     *     Bit sync (D1)          : [ 0  20ms ]   : STATE_BIT_SYNC is set
-     *     Subframe (D2)          : [ 0  0.6s ]   : STATE_BDS_D2_SUBFRAME_SYNC is set
-     *     Subframe (D1)          : [ 0    6s ]   : STATE_SUBFRAME_SYNC is set
-     *     Time of week decoded   : [ 0 1week ]   : STATE_TOW_DECODED is set
-     *     Time of week known     : [ 0 1week ]   : STATE_TOW_KNOWN set</pre>
-     *
-     * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
-     * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
-     *
-     * <p>For Galileo, this is:
-     * <ul>
-     * <li>Received Galileo time of week, at the measurement time in nanoseconds.</li>
-     * </ul>
-     * <pre>
-     *     E1BC code lock       : [ 0   4ms ]  : STATE_GAL_E1BC_CODE_LOCK is set
-     *     E1C 2nd code lock    : [ 0 100ms ]  : STATE_GAL_E1C_2ND_CODE_LOCK is set
-     *     E1B page             : [ 0    2s ]  : STATE_GAL_E1B_PAGE_SYNC is set
-     *     Time of week decoded : [ 0 1week ]  : STATE_TOW_DECODED is set
-     *     Time of week known   : [ 0 1week ]  : STATE_TOW_KNOWN set</pre>
-     *
-     * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
-     * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
-     *
-     * <p>For SBAS, this is:
-     * <ul>
-     * <li>Received SBAS time, at the measurement time in nanoseconds.</li>
-     * </ul>
-     *
-     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
-     * this field can be:
-     * <pre>
-     *     Searching       : [ 0       ]   : STATE_UNKNOWN
-     *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
-     *     Symbol sync     : [ 0   2ms ]   : STATE_SYMBOL_SYNC is set
-     *     Message         : [ 0    1s ]   : STATE_SBAS_SYNC is set</pre>
      */
     public long getReceivedSvTimeNanos() {
         return mReceivedSvTimeNanos;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1aeefb8..57a0a72 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -114,6 +114,7 @@
     // for reporting callback completion
     void locationCallbackFinished(ILocationListener listener);
 
-    // used by gts tests to verify throttling whitelist
+    // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
+    String[] getIgnoreSettingsWhitelist();
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c027fd4..586ee2a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -414,6 +414,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getIgnoreSettingsWhitelist() {
+        try {
+            return mService.getIgnoreSettingsWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0caa0c5..b3953fd 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -184,8 +184,7 @@
      * @return a new location request
      */
     public static LocationRequest create() {
-        LocationRequest request = new LocationRequest();
-        return request;
+        return new LocationRequest();
     }
 
     /** @hide */
@@ -230,12 +229,10 @@
                 quality = ACCURACY_FINE;
                 break;
             default: {
-                switch (criteria.getPowerRequirement()) {
-                    case Criteria.POWER_HIGH:
-                        quality = POWER_HIGH;
-                        break;
-                    default:
-                        quality = POWER_LOW;
+                if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
+                    quality = POWER_HIGH;
+                } else {
+                    quality = POWER_LOW;
                 }
             }
         }
@@ -288,7 +285,7 @@
      *
      * @param quality an accuracy or power constant
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the quality constant is not valid
+     * @throws IllegalArgumentException if the quality constant is not valid
      */
     public LocationRequest setQuality(int quality) {
         checkQuality(quality);
@@ -331,7 +328,7 @@
      *
      * @param millis desired interval in millisecond, inexact
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the interval is less than zero
+     * @throws IllegalArgumentException if the interval is less than zero
      */
     public LocationRequest setInterval(long millis) {
         checkInterval(millis);
@@ -433,7 +430,7 @@
      *
      * @param millis fastest interval for updates in milliseconds, exact
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the interval is less than zero
+     * @throws IllegalArgumentException if the interval is less than zero
      */
     public LocationRequest setFastestInterval(long millis) {
         checkInterval(millis);
@@ -528,7 +525,7 @@
      *
      * @param numUpdates the number of location updates requested
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if numUpdates is 0 or less
+     * @throws IllegalArgumentException if numUpdates is 0 or less
      */
     public LocationRequest setNumUpdates(int numUpdates) {
         if (numUpdates <= 0) {
@@ -668,7 +665,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkProvider(String name) {
         if (name == null) {
-            throw new IllegalArgumentException("invalid provider: " + name);
+            throw new IllegalArgumentException("invalid provider: null");
         }
     }
 
@@ -758,9 +755,11 @@
         if (mNumUpdates != Integer.MAX_VALUE) {
             s.append(" num=").append(mNumUpdates);
         }
-        s.append(" lowPowerMode=").append(mLowPowerMode);
+        if (mLowPowerMode) {
+            s.append(" lowPowerMode");
+        }
         if (mLocationSettingsIgnored) {
-            s.append(" ignoreSettings");
+            s.append(" locationSettingsIgnored");
         }
         s.append(']');
         return s.toString();
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index a45c20d..af8123a 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -40,7 +40,7 @@
      * restrictions or any other restricting factors and always satisfy this request to the best of
      * their ability. This flag should only be used in event of an emergency.
      */
-    public boolean forceLocation = false;
+    public boolean locationSettingsIgnored = false;
 
     /**
      * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
@@ -70,6 +70,7 @@
                     request.reportLocation = in.readInt() == 1;
                     request.interval = in.readLong();
                     request.lowPowerMode = in.readBoolean();
+                    request.locationSettingsIgnored = in.readBoolean();
                     int count = in.readInt();
                     for (int i = 0; i < count; i++) {
                         request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
@@ -93,6 +94,7 @@
         parcel.writeInt(reportLocation ? 1 : 0);
         parcel.writeLong(interval);
         parcel.writeBoolean(lowPowerMode);
+        parcel.writeBoolean(locationSettingsIgnored);
         parcel.writeInt(locationRequests.size());
         for (LocationRequest request : locationRequests) {
             request.writeToParcel(parcel, flags);
@@ -107,7 +109,12 @@
             s.append("ON");
             s.append(" interval=");
             TimeUtils.formatDuration(interval, s);
-            s.append(" lowPowerMode=" + lowPowerMode);
+            if (lowPowerMode) {
+                s.append(" lowPowerMode");
+            }
+            if (locationSettingsIgnored) {
+                s.append(" locationSettingsIgnored");
+            }
         } else {
             s.append("OFF");
         }
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 67d6496..dbb581f 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -31,6 +31,7 @@
     method public long getInterval();
     method public int getQuality();
     method public float getSmallestDisplacement();
+    method public boolean isLocationSettingsIgnored();
     field public static final int ACCURACY_BLOCK = 102; // 0x66
     field public static final int ACCURACY_CITY = 104; // 0x68
     field public static final int ACCURACY_FINE = 100; // 0x64
@@ -44,10 +45,10 @@
   }
 
   public final class ProviderRequestUnbundled {
-    method public boolean getForceLocation();
     method public long getInterval();
     method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
     method public boolean getReportLocation();
+    method public boolean isLocationSettingsIgnored();
   }
 
 }
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 41fd769..2511c39 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -121,6 +121,15 @@
         return delegate.getSmallestDisplacement();
     }
 
+    /**
+     * Returns true if location settings will be ignored in order to satisfy this request.
+     *
+     * @return true if location settings will be ignored in order to satisfy this request
+     */
+    public boolean isLocationSettingsIgnored() {
+        return delegate.isLocationSettingsIgnored();
+    }
+
     @Override
     public String toString() {
       return delegate.toString();
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index b825b58..febbf1b 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -46,15 +46,15 @@
         return mRequest.interval;
     }
 
-    public boolean getForceLocation() {
-        return mRequest.forceLocation;
+    public boolean isLocationSettingsIgnored() {
+        return mRequest.locationSettingsIgnored;
     }
 
     /**
      * Never null.
      */
     public List<LocationRequestUnbundled> getLocationRequests() {
-        List<LocationRequestUnbundled> result = new ArrayList<LocationRequestUnbundled>(
+        List<LocationRequestUnbundled> result = new ArrayList<>(
                 mRequest.locationRequests.size());
         for (LocationRequest r : mRequest.locationRequests) {
           result.add(new LocationRequestUnbundled(r));
diff --git a/media/Android.bp b/media/Android.bp
index 141d415c..86dc509 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -3,7 +3,6 @@
 
     srcs: [
         ":updatable-media-srcs",
-        ":framework-media-annotation-srcs",
     ],
 
     aidl: {
@@ -28,7 +27,12 @@
     installable: true,
 
     // Make sure that the implementaion only relies on SDK or system APIs.
-    sdk_version: "system_current",
+    no_framework_libs: true,
+    libs: [
+        // The order matters. android_system_* library should come later.
+        "framework_media_annotation",
+        "android_system_stubs_current",
+    ],
 }
 
 filegroup {
@@ -125,3 +129,8 @@
     sdk_version: "28",
 }
 
+java_library {
+    name: "framework_media_annotation",
+    srcs: [":framework-media-annotation-srcs"],
+    installable: false,
+}
diff --git a/media/OWNERS b/media/OWNERS
index 03b751c..eb26367 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -11,3 +11,6 @@
 marcone@google.com
 sungsoo@google.com
 wjia@google.com
+
+# For maintaining sync with AndroidX code
+per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 887b447..4ea384a 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -317,7 +317,9 @@
                 isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
             }
             if (isCanceled) {
-                resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                if (resultReceiver != null) {
+                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                }
                 return;
             }
             Session2Command.Result result = mCallback.onSessionCommand(
@@ -425,7 +427,7 @@
         public void onDisconnected(@NonNull MediaController2 controller) {}
 
         /**
-         * Called when the playback of the session's playback activeness is changed.
+         * Called when the session's playback activeness is changed.
          *
          * @param controller the controller for this event
          * @param playbackActive {@code true} if the session's playback is active.
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index c38a831..89a9540 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -45,8 +45,8 @@
 import android.view.Surface;
 import android.view.SurfaceHolder;
 
-import com.android.framework.protobuf.InvalidProtocolBufferException;
 import com.android.internal.annotations.GuardedBy;
+import com.android.media.protobuf.InvalidProtocolBufferException;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index fdd07fd..4c6945a 100644
--- a/media/apex/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
@@ -259,6 +259,20 @@
         }
     }
 
+    /**
+     * Gets the list of the connected controllers
+     *
+     * @return list of the connected controllers.
+     */
+    @NonNull
+    public List<ControllerInfo> getConnectedControllers() {
+        List<ControllerInfo> controllers = new ArrayList<>();
+        synchronized (mLock) {
+            controllers.addAll(mConnectedControllers.values());
+        }
+        return controllers;
+    }
+
     boolean isClosed() {
         synchronized (mLock) {
             return mClosed;
@@ -317,13 +331,6 @@
                 if (DEBUG) {
                     Log.d(TAG, "Accepting connection: " + controllerInfo);
                 }
-                synchronized (mLock) {
-                    if (mConnectedControllers.containsKey(controller)) {
-                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
-                                + " request multiple times");
-                    }
-                    mConnectedControllers.put(controller, controllerInfo);
-                }
                 // If connection is accepted, notify the current state to the controller.
                 // It's needed because we cannot call synchronous calls between
                 // session/controller.
@@ -339,6 +346,14 @@
                     return;
                 }
                 controllerInfo.notifyConnected(connectionResult);
+                synchronized (mLock) {
+                    if (mConnectedControllers.containsKey(controller)) {
+                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
+                                + " request multiple times");
+                    }
+                    mConnectedControllers.put(controller, controllerInfo);
+                }
+                mCallback.onPostConnect(MediaSession2.this, controllerInfo);
                 connected = true;
             } finally {
                 if (!connected) {
@@ -417,14 +432,6 @@
         controllerInfo.removeRequestedCommandSeqNumber(seq);
     }
 
-    private List<ControllerInfo> getConnectedControllers() {
-        List<ControllerInfo> controllers = new ArrayList<>();
-        synchronized (mLock) {
-            controllers.addAll(mConnectedControllers.values());
-        }
-        return controllers;
-    }
-
     /**
      * Builder for {@link MediaSession2}.
      * <p>
@@ -738,6 +745,17 @@
         }
 
         /**
+         * Called immediately after a controller is connected. This is a convenient method to add
+         * custom initialization between the session and a controller.
+         *
+         * @param session the session for this event
+         * @param controller controller information.
+         */
+        public void onPostConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
+        }
+
+        /**
          * Called when a controller is disconnected
          *
          * @param session the session for this event
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 0a9ca02..3594ee7 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,10 +30,10 @@
 @SystemApi
 public final class AudioFocusInfo implements Parcelable {
 
-    private final AudioAttributes mAttributes;
+    private final @NonNull AudioAttributes mAttributes;
     private final int mClientUid;
-    private final String mClientId;
-    private final String mPackageName;
+    private final @NonNull String mClientId;
+    private final @NonNull String mPackageName;
     private final int mSdkTarget;
     private int mGainRequest;
     private int mLossReceived;
@@ -80,13 +81,21 @@
      * The audio attributes for the audio focus request.
      * @return non-null {@link AudioAttributes}.
      */
-    public AudioAttributes getAttributes() { return mAttributes; }
+    public @NonNull AudioAttributes getAttributes() {
+        return mAttributes;
+    }
 
-    public int getClientUid() { return mClientUid; }
+    public int getClientUid() {
+        return mClientUid;
+    }
 
-    public String getClientId() { return mClientId; }
+    public @NonNull String getClientId() {
+        return mClientId;
+    }
 
-    public String getPackageName() { return mPackageName; }
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
 
     /**
      * The type of audio focus gain request.
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index b9731d1..4e70501 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -225,9 +225,9 @@
     /** @hide */
     public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking";
 
-    private final OnAudioFocusChangeListener mFocusListener; // may be null
-    private final Handler mListenerHandler;                  // may be null
-    private final AudioAttributes mAttr;                     // never null
+    private final @Nullable OnAudioFocusChangeListener mFocusListener;
+    private final @Nullable Handler mListenerHandler;
+    private final @NonNull AudioAttributes mAttr;
     private final int mFocusGain;
     private final int mFlags;
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 92afe7e..24a3a9b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1713,12 +1714,11 @@
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    public int setMicrophoneDirection(int direction) {
-        return native_set_microphone_direction(direction);
+    public boolean setMicrophoneDirection(int direction) {
+        return native_set_microphone_direction(direction) == 0;
     }
 
     /**
@@ -1727,11 +1727,10 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    public int setMicrophoneFieldDimension(float zoom) {
-        return native_set_microphone_field_dimension(zoom);
+    public boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom) {
+        return native_set_microphone_field_dimension(zoom) == 0;
     }
 
     //---------------------------------------------------------
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
new file mode 100644
index 0000000..8bdb8a6
--- /dev/null
+++ b/media/java/android/media/HwAudioSource.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The HwAudioSource represents the audio playback directly from a source audio device.
+ * It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only
+ * corresponding to {@link AudioSystem#startAudioSource(AudioPortConfig, AudioAttributes)}
+ * and {@link AudioSystem#stopAudioSource(int)}.
+ *
+ * @hide
+ */
+@SystemApi
+public class HwAudioSource extends PlayerBase {
+    private final AudioDeviceInfo mAudioDeviceInfo;
+    private final AudioAttributes mAudioAttributes;
+
+    private int mNativeHandle;
+
+    /**
+     * Class constructor for a hardware audio source based player.
+     *
+     * Use the {@link Builder} class to construct a {@link HwAudioSource} instance.
+     *
+     * @param device {@link AudioDeviceInfo} instance of the source audio device.
+     * @param attributes {@link AudioAttributes} instance for this player.
+     */
+    private HwAudioSource(@NonNull AudioDeviceInfo device, @NonNull AudioAttributes attributes) {
+        super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_HW_SOURCE);
+        Preconditions.checkNotNull(device);
+        Preconditions.checkNotNull(attributes);
+        Preconditions.checkArgument(device.isSource(), "Requires a source device");
+        mAudioDeviceInfo = device;
+        mAudioAttributes = attributes;
+        baseRegisterPlayer();
+    }
+
+    /**
+     * TODO: sets the gain on {@link #mAudioDeviceInfo}.
+     *
+     * @param muting if true, the player is to be muted, and the volume values can be ignored
+     * @param leftVolume the left volume to use if muting is false
+     * @param rightVolume the right volume to use if muting is false
+     */
+    @Override
+    void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
+    }
+
+    /**
+     * TODO: applies {@link VolumeShaper} on {@link #mAudioDeviceInfo}.
+     *
+     * @param configuration a {@code VolumeShaper.Configuration} object
+     *        created by {@link VolumeShaper.Configuration.Builder} or
+     *        an created from a {@code VolumeShaper} id
+     *        by the {@link VolumeShaper.Configuration} constructor.
+     * @param operation a {@code VolumeShaper.Operation}.
+     * @return
+     */
+    @Override
+    int playerApplyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation) {
+        return 0;
+    }
+
+    /**
+     * TODO: gets the {@link VolumeShaper} by a given id.
+     *
+     * @param id the {@code VolumeShaper} id returned from
+     *           sending a fully specified {@code VolumeShaper.Configuration}
+     *           through {@link #playerApplyVolumeShaper}
+     * @return
+     */
+    @Override
+    @Nullable
+    VolumeShaper.State playerGetVolumeShaperState(int id) {
+        return new VolumeShaper.State(1f, 1f);
+    }
+
+    /**
+     * TODO: sets the level on {@link #mAudioDeviceInfo}.
+     *
+     * @param muting
+     * @param level
+     * @return
+     */
+    @Override
+    int playerSetAuxEffectSendLevel(boolean muting, float level) {
+        return AudioSystem.SUCCESS;
+    }
+
+    @Override
+    void playerStart() {
+        start();
+    }
+
+    @Override
+    void playerPause() {
+        // Pause is equivalent to stop for hardware audio source based players.
+        stop();
+    }
+
+    @Override
+    void playerStop() {
+        stop();
+    }
+
+    /**
+     * Starts the playback from {@link AudioDeviceInfo}.
+     */
+    public void start() {
+        baseStart();
+        mNativeHandle = AudioSystem.startAudioSource(
+                mAudioDeviceInfo.getPort().activeConfig(),
+                mAudioAttributes);
+    }
+
+    /**
+     * Stops the playback from {@link AudioDeviceInfo}.
+     */
+    public void stop() {
+        baseStop();
+        if (mNativeHandle > 0) {
+            AudioSystem.stopAudioSource(mNativeHandle);
+            mNativeHandle = 0;
+        }
+    }
+
+    /**
+     * Builder class for {@link HwAudioSource} objects.
+     * Use this class to configure and create a <code>HwAudioSource</code> instance.
+     * <p>Here is an example where <code>Builder</code> is used to specify an audio
+     * playback directly from a source device as media usage, to be used by a new
+     * <code>HwAudioSource</code> instance:
+     *
+     * <pre class="prettyprint">
+     * HwAudioSource player = new HwAudioSource.Builder()
+     *              .setAudioAttributes(new AudioAttributes.Builder()
+     *                       .setUsage(AudioAttributes.USAGE_MEDIA)
+     *                       .build())
+     *              .setAudioDeviceInfo(device)
+     *              .build()
+     * </pre>
+     * <p>
+     * If the audio attributes are not set with {@link #setAudioAttributes(AudioAttributes)},
+     * attributes comprising {@link AudioAttributes#USAGE_MEDIA} will be used.
+     */
+    public static class Builder {
+        private AudioAttributes mAudioAttributes;
+        private AudioDeviceInfo mAudioDeviceInfo;
+
+        /**
+         * Constructs a new Builder with default values.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the {@link AudioAttributes}.
+         * @param attributes a non-null {@link AudioAttributes} instance that describes the audio
+         *     data to be played.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) {
+            Preconditions.checkNotNull(attributes);
+            mAudioAttributes = attributes;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AudioDeviceInfo}.
+         * @param info a non-null {@link AudioDeviceInfo} instance that describes the audio
+         *     data come from.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setAudioDeviceInfo(@NonNull AudioDeviceInfo info) {
+            Preconditions.checkNotNull(info);
+            Preconditions.checkArgument(info.isSource());
+            mAudioDeviceInfo = info;
+            return this;
+        }
+
+        /**
+         * Builds an {@link HwAudioSource} instance initialized with all the parameters set
+         * on this <code>Builder</code>.
+         * @return a new successfully initialized {@link HwAudioSource} instance.
+         */
+        public @NonNull HwAudioSource build() {
+            Preconditions.checkNotNull(mAudioDeviceInfo);
+            if (mAudioAttributes == null) {
+                mAudioAttributes = new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_MEDIA)
+                        .build();
+            }
+            return new HwAudioSource(mAudioDeviceInfo, mAudioAttributes);
+        }
+    }
+
+    /**
+     * Eliminate {@link #deprecateStreamTypeForPlayback(int, String, String)} in API list.
+     * TODO: remove this pseudo-override function
+     * @hide
+     */
+    public static void deprecateStreamTypeForPlayback(int streamType, String className,
+            String opName) throws IllegalArgumentException {
+        // Do nothing.
+    }
+}
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index ad25a06..5f324f7 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkUtils;
 import android.os.IBinder;
@@ -23,21 +25,19 @@
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.NoRouteToHostException;
 import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
-
-import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /** @hide */
 public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -67,6 +67,7 @@
     // from com.squareup.okhttp.internal.http
     private final static int HTTP_TEMP_REDIRECT = 307;
     private final static int MAX_REDIRECTS = 20;
+    private AtomicBoolean mIsConnected = new AtomicBoolean(false);
 
     @UnsupportedAppUsage
     public MediaHTTPConnection() {
@@ -90,6 +91,7 @@
             mAllowCrossDomainRedirect = true;
             mURL = new URL(uri);
             mHeaders = convertHeaderStringToMap(headers);
+            mIsConnected.set(true);
         } catch (MalformedURLException e) {
             return null;
         }
@@ -140,7 +142,14 @@
     @Override
     @UnsupportedAppUsage
     public void disconnect() {
-        teardownConnection();
+        if (mIsConnected.getAndSet(false)) {
+            (new Thread() {
+                @Override
+                public void run() {
+                    teardownConnection();
+                }
+            }).start();
+        }
         mHeaders = null;
         mURL = null;
     }
@@ -325,7 +334,14 @@
     @Override
     @UnsupportedAppUsage
     public int readAt(long offset, int size) {
-        return native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        int result = native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        return result;
     }
 
     private int readAt(long offset, byte[] data, int size) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fb18c3b..e4d356b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1180,13 +1180,8 @@
         }
 
         final File file = new File(path);
-        if (file.exists()) {
-            FileInputStream is = new FileInputStream(file);
-            FileDescriptor fd = is.getFD();
-            setDataSource(fd);
-            is.close();
-        } else {
-            throw new IOException("setDataSource failed.");
+        try (FileInputStream is = new FileInputStream(file)) {
+            setDataSource(is.getFD());
         }
     }
 
@@ -2868,15 +2863,9 @@
             throw new IllegalArgumentException(msg);
         }
 
-        File file = new File(path);
-        if (file.exists()) {
-            FileInputStream is = new FileInputStream(file);
-            FileDescriptor fd = is.getFD();
-            addTimedTextSource(fd, mimeType);
-            is.close();
-        } else {
-            // We do not support the case where the path is not a file.
-            throw new IOException(path);
+        final File file = new File(path);
+        try (FileInputStream is = new FileInputStream(file)) {
+            addTimedTextSource(is.getFD(), mimeType);
         }
     }
 
diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java
index 99201c0..489e268 100644
--- a/media/java/android/media/MicrophoneDirection.java
+++ b/media/java/android/media/MicrophoneDirection.java
@@ -16,38 +16,46 @@
 
 package android.media;
 
-/**
- * @hide
- */
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public interface MicrophoneDirection {
     /**
-     * @hide
+     * Don't do any directionality processing of the activated microphone(s).
      */
     int MIC_DIRECTION_UNSPECIFIED = 0;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the screen-side of the device.
      */
     int MIC_DIRECTION_FRONT = 1;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the side of the device opposite the screen.
      */
     int MIC_DIRECTION_BACK = 2;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from an off-device microphone.
      */
     int MIC_DIRECTION_EXTERNAL = 3;
 
+    /** @hide */
+    @IntDef({
+            MIC_DIRECTION_UNSPECIFIED,
+            MIC_DIRECTION_FRONT,
+            MIC_DIRECTION_BACK,
+            MIC_DIRECTION_EXTERNAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Directionmode{};
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    int setMicrophoneDirection(int direction);
+    boolean setMicrophoneDirection(@Directionmode int direction);
 
     /**
      * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
@@ -55,8 +63,7 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    int setMicrophoneFieldDimension(float zoom);
+    boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom);
 }
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index ccf49bd..21b194d 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -122,6 +122,9 @@
      * @param filePath The audio file.
      * @param kind The desired thumbnail kind, such as
      *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+     * @deprecated Callers should migrate to using
+     *             {@link #createAudioThumbnail(File, Size, CancellationSignal)},
+     *             as it offers more control over resizing and cancellation.
      */
     @Deprecated
     public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) {
@@ -211,6 +214,9 @@
      * @param filePath The image file.
      * @param kind The desired thumbnail kind, such as
      *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+     * @deprecated Callers should migrate to using
+     *             {@link #createImageThumbnail(File, Size, CancellationSignal)},
+     *             as it offers more control over resizing and cancellation.
      */
     @Deprecated
     public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) {
@@ -270,6 +276,9 @@
      * @param filePath The video file.
      * @param kind The desired thumbnail kind, such as
      *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+     * @deprecated Callers should migrate to using
+     *             {@link #createVideoThumbnail(File, Size, CancellationSignal)},
+     *             as it offers more control over resizing and cancellation.
      */
     @Deprecated
     public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) {
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.aidl b/media/java/android/media/audiopolicy/AudioProductStrategies.aidl
new file mode 100644
index 0000000..bec11bc
--- /dev/null
+++ b/media/java/android/media/audiopolicy/AudioProductStrategies.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.audiopolicy;
+
+parcelable AudioProductStrategies;
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java
new file mode 100644
index 0000000..b836409
--- /dev/null
+++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiopolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @hide
+ * A class to encapsulate a collection of {@link AudioProductStrategy}.
+ * Provides helper functions to easily retrieve the {@link AudioAttributes} for a given product
+ * strategy or legacy stream type.
+ */
+@SystemApi
+public final class AudioProductStrategies implements Iterable<AudioProductStrategy>, Parcelable {
+
+    private final ArrayList<AudioProductStrategy> mAudioProductStrategyList;
+
+    private static final String TAG = "AudioProductStrategies";
+
+    public AudioProductStrategies() {
+        ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
+        int status = native_list_audio_product_strategies(apsList);
+        if (status != AudioSystem.SUCCESS) {
+            Log.w(TAG, ": createAudioProductStrategies failed");
+        }
+        mAudioProductStrategyList = apsList;
+    }
+
+    private AudioProductStrategies(ArrayList<AudioProductStrategy> audioProductStrategy) {
+        mAudioProductStrategyList = audioProductStrategy;
+    }
+
+    /**
+     * @hide
+     * @return number of {@link AudioProductStrategy} objects
+     */
+    @SystemApi
+    public int size() {
+        return mAudioProductStrategyList.size();
+    }
+
+    /**
+     * @hide
+     * @return the matching {@link AudioProductStrategy} objects with the given id,
+     *         null object if not found.
+     */
+    @SystemApi
+    public @Nullable AudioProductStrategy getById(int productStrategyId) {
+        for (final AudioProductStrategy avg : this) {
+            if (avg.getId() == productStrategyId) {
+                return avg;
+            }
+        }
+        Log.e(TAG, ": invalid product strategy id: " + productStrategyId + " requested");
+        return null;
+    }
+
+    /**
+     * Returns an {@link Iterator}
+     */
+    @Override
+    public Iterator<AudioProductStrategy> iterator() {
+        return mAudioProductStrategyList.iterator();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AudioProductStrategies that = (AudioProductStrategies) o;
+
+        return mAudioProductStrategyList.equals(that.mAudioProductStrategyList);
+    }
+
+    /**
+     * @hide
+     * @param aps {@link AudioProductStrategy} (which is the generalisation of Car Audio Usage /
+     *                        legacy routing_strategy linked to {@link AudioAttributes#getUsage()} )
+     * @return the {@link AudioAttributes} relevant for the given product strategy.
+     *         If none is found, it builds the default attributes.
+     *         TODO: shall the helper collection be able to identify the platform default?
+     */
+    @SystemApi
+    @NonNull
+    public AudioAttributes getAudioAttributesForProductStrategy(@NonNull AudioProductStrategy aps) {
+        Preconditions.checkNotNull(aps, "AudioProductStrategy must not be null");
+        for (final AudioProductStrategy audioProductStrategy : this) {
+            if (audioProductStrategy.equals(aps)) {
+                return audioProductStrategy.getAudioAttributes();
+            }
+        }
+        return new AudioAttributes.Builder()
+                                  .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                                  .setUsage(AudioAttributes.USAGE_UNKNOWN).build();
+    }
+
+    /**
+     * @hide
+     * @param streamType legacy stream type used for volume operation only
+     * @return the {@link AudioAttributes} relevant for the given streamType.
+     *         If none is found, it builds the default attributes.
+     */
+    @SystemApi
+    public @NonNull AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) {
+        for (final AudioProductStrategy productStrategy : this) {
+            AudioAttributes aa = productStrategy.getAudioAttributesForLegacyStreamType(streamType);
+            if (aa != null) {
+                return aa;
+            }
+        }
+        return new AudioAttributes.Builder()
+                                  .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                                  .setUsage(AudioAttributes.USAGE_UNKNOWN).build();
+    }
+
+    /**
+     * @hide
+     * @param aa the {@link AudioAttributes} for which stream type is requested
+     * @return the legacy stream type relevant for the given {@link AudioAttributes}.
+     *         If the product strategy is not associated to any stream, it returns STREAM_MUSIC.
+     *         If no product strategy supports the stream type, it returns STREAM_MUSIC.
+     */
+    @SystemApi
+    public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
+        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        for (final AudioProductStrategy productStrategy : this) {
+            if (productStrategy.supportsAudioAttributes(aa)) {
+                int streamType = productStrategy.getLegacyStreamTypeForAudioAttributes(aa);
+                if (streamType == AudioSystem.STREAM_DEFAULT) {
+                    Log.w(TAG, "Attributes " + aa.toString() + " ported by strategy "
+                            + productStrategy.name() + " has no stream type associated, "
+                            + "DO NOT USE STREAM TO CONTROL THE VOLUME");
+                    return AudioSystem.STREAM_MUSIC;
+                }
+                return streamType;
+            }
+        }
+        return AudioSystem.STREAM_MUSIC;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(size());
+        for (final AudioProductStrategy productStrategy : this) {
+            productStrategy.writeToParcel(dest, flags);
+        }
+    }
+
+    public static final Parcelable.Creator<AudioProductStrategies> CREATOR =
+            new Parcelable.Creator<AudioProductStrategies>() {
+                @Override
+                public AudioProductStrategies createFromParcel(@NonNull Parcel in) {
+                    ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
+                    int size = in.readInt();
+                    for (int index = 0; index < size; index++) {
+                        apsList.add(AudioProductStrategy.CREATOR.createFromParcel(in));
+                    }
+                    return new AudioProductStrategies(apsList);
+                }
+
+                @Override
+                public @NonNull AudioProductStrategies[] newArray(int size) {
+                    return new AudioProductStrategies[size];
+                }
+            };
+
+    private static native int native_list_audio_product_strategies(
+            ArrayList<AudioProductStrategy> strategies);
+}
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.aidl b/media/java/android/media/audiopolicy/AudioProductStrategy.aidl
new file mode 100644
index 0000000..5ead30b
--- /dev/null
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.audiopolicy;
+
+parcelable AudioProductStrategy;
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
new file mode 100644
index 0000000..af6e8bf
--- /dev/null
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -0,0 +1,378 @@
+/*
+ * 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.media.audiopolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.MediaRecorder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * @hide
+ * A class to encapsulate a collection of attributes associated to a given product strategy
+ * (and for legacy reason, keep the association with the stream type).
+ */
+@SystemApi
+public final class AudioProductStrategy implements Parcelable {
+    /**
+     * group value to use when introspection API fails.
+     * @hide
+     */
+    public static final int DEFAULT_GROUP = -1;
+
+    private final AudioAttributesGroup[] mAudioAttributesGroups;
+    private final String mName;
+    /**
+     * Unique identifier of a product strategy.
+     * This Id can be assimilated to Car Audio Usage and even more generally to usage.
+     * For legacy platforms, the product strategy id is the routing_strategy, which was hidden to
+     * upper layer but was transpiring in the {@link AudioAttributes#getUsage()}.
+     */
+    private int mId;
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AudioProductStrategy thatStrategy = (AudioProductStrategy) o;
+
+        return mName == thatStrategy.mName && mId == thatStrategy.mId
+                && mAudioAttributesGroups.equals(thatStrategy.mAudioAttributesGroups);
+    }
+
+    /**
+     * @param name of the product strategy
+     * @param id of the product strategy
+     * @param audioAttributes {@link AudioAttributes} associated to the given product strategy
+     * @param legacyStreamTypes associated to the given product strategy.
+     */
+    private AudioProductStrategy(@NonNull String name, int id,
+            @NonNull AudioAttributesGroup[] aag) {
+        Preconditions.checkNotNull(name, "name must not be null");
+        Preconditions.checkNotNull(aag, "AudioAttributesGroups must not be null");
+        mName = name;
+        mId = id;
+        mAudioAttributesGroups = aag;
+    }
+
+    /**
+     * @hide
+     * @return human-readable name of this product strategy, which is similar to a usage
+     */
+    @SystemApi
+    public @NonNull String name() {
+        return mName;
+    }
+
+    /**
+     * @hide
+     * @return the product strategy ID (which is the generalisation of Car Audio Usage / legacy
+     *         routing_strategy linked to {@link AudioAttributes#getUsage()}).
+     */
+    @SystemApi
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * @hide
+     * @return first {@link AudioAttributes} associated to this product strategy.
+     */
+    @SystemApi
+    public @NonNull AudioAttributes getAudioAttributes() {
+        // We need a choice, so take the first one
+        return mAudioAttributesGroups.length == 0 ? (new AudioAttributes.Builder().build())
+                : mAudioAttributesGroups[0].getAudioAttributes();
+    }
+
+    /**
+     * @hide
+     * @param streamType legacy stream type used for volume operation only
+     * @return the {@link AudioAttributes} relevant for the given streamType.
+     *         If none is found, it builds the default attributes.
+     */
+    public @Nullable AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) {
+        for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
+            if (aag.supportsStreamType(streamType)) {
+                return aag.getAudioAttributes();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     * @param aa the {@link AudioAttributes} to be considered
+     * @return the legacy stream type relevant for the given {@link AudioAttributes}.
+     *         If none is found, it return DEFAULT stream type.
+     */
+    public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
+        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
+            if (aag.supportsAttributes(aa)) {
+                return aag.getStreamType();
+            }
+        }
+        return AudioSystem.STREAM_DEFAULT;
+    }
+
+    /**
+     * @hide
+     * @param aa the {@link AudioAttributes} to be considered
+     * @return true if the {@link AudioProductStrategy} supports the given {@link AudioAttributes},
+     *         false otherwise.
+     */
+    public boolean supportsAudioAttributes(@NonNull AudioAttributes aa) {
+        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
+            if (aag.supportsAttributes(aa)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     * @param streamType legacy stream type used for volume operation only
+     * @return the {@link AudioAttributes} relevant for the given streamType.
+     *         If none is found, it builds the default attributes.
+     */
+    public int getGroupIdForLegacyStreamType(int streamType) {
+        for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
+            if (aag.supportsStreamType(streamType)) {
+                return aag.getGroupId();
+            }
+        }
+        return DEFAULT_GROUP;
+    }
+
+    /**
+     * @hide
+     * @param aa the {@link AudioAttributes} to be considered
+     * @return the group id associated with the given audio attributes if found,
+     *         default value otherwise.
+     */
+    public int getGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
+        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
+            if (aag.supportsAttributes(aa)) {
+                return aag.getGroupId();
+            }
+        }
+        return DEFAULT_GROUP;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mName);
+        dest.writeInt(mId);
+        dest.writeInt(mAudioAttributesGroups.length);
+        for (AudioAttributesGroup aag : mAudioAttributesGroups) {
+            aag.writeToParcel(dest, flags);
+        }
+    }
+
+    public static final Parcelable.Creator<AudioProductStrategy> CREATOR =
+            new Parcelable.Creator<AudioProductStrategy>() {
+                @Override
+                public AudioProductStrategy createFromParcel(@NonNull Parcel in) {
+                    String name = in.readString();
+                    int id = in.readInt();
+                    int nbAttributesGroups = in.readInt();
+                    AudioAttributesGroup[] aag = new AudioAttributesGroup[nbAttributesGroups];
+                    for (int index = 0; index < nbAttributesGroups; index++) {
+                        aag[index] = AudioAttributesGroup.CREATOR.createFromParcel(in);
+                    }
+                    return new AudioProductStrategy(name, id, aag);
+                }
+
+                @Override
+                public @NonNull AudioProductStrategy[] newArray(int size) {
+                    return new AudioProductStrategy[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("\n Name: ");
+        s.append(mName);
+        s.append(" Id: ");
+        s.append(Integer.toString(mId));
+        for (AudioAttributesGroup aag : mAudioAttributesGroups) {
+            s.append(aag.toString());
+        }
+        return s.toString();
+    }
+
+    /**
+     * @hide
+     * Default attributes, with default source to be aligned with native.
+     */
+    public static final @NonNull AudioAttributes sDefaultAttributes =
+            new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
+                                         .build();
+
+    /**
+     * To avoid duplicating the logic in java and native, we shall make use of
+     * native API native_get_product_strategies_from_audio_attributes
+     * @param refAttr {@link AudioAttributes} to be taken as the reference
+     * @param attr {@link AudioAttributes} of the requester.
+     */
+    private static boolean attributesMatches(@NonNull AudioAttributes refAttr,
+            @NonNull AudioAttributes attr) {
+        Preconditions.checkNotNull(refAttr, "refAttr must not be null");
+        Preconditions.checkNotNull(attr, "attr must not be null");
+        String refFormattedTags = TextUtils.join(";", refAttr.getTags());
+        String cliFormattedTags = TextUtils.join(";", attr.getTags());
+        if (refAttr.equals(sDefaultAttributes)) {
+            return false;
+        }
+        return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN)
+                || (attr.getUsage() == refAttr.getUsage()))
+            && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                || (attr.getContentType() == refAttr.getContentType()))
+            && ((refAttr.getAllFlags() == 0)
+                || (attr.getAllFlags() != 0
+                && (attr.getAllFlags() & refAttr.getAllFlags()) == attr.getAllFlags()))
+            && ((refFormattedTags.length() == 0) || refFormattedTags.equals(cliFormattedTags));
+    }
+
+
+    private static final class AudioAttributesGroup implements Parcelable {
+        private int mGroupId;
+        private int mLegacyStreamType;
+        private final AudioAttributes[] mAudioAttributes;
+
+        AudioAttributesGroup(int groupId, int streamType,
+                @NonNull AudioAttributes[] audioAttributes) {
+            mGroupId = groupId;
+            mLegacyStreamType = streamType;
+            mAudioAttributes = audioAttributes;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            AudioAttributesGroup thatAag = (AudioAttributesGroup) o;
+
+            return mGroupId == thatAag.mGroupId
+                    && mLegacyStreamType == thatAag.mLegacyStreamType
+                    && mAudioAttributes.equals(thatAag.mAudioAttributes);
+        }
+
+        public int getStreamType() {
+            return mLegacyStreamType;
+        }
+
+        public int getGroupId() {
+            return mGroupId;
+        }
+
+        public @NonNull AudioAttributes getAudioAttributes() {
+            // We need a choice, so take the first one
+            return mAudioAttributes.length == 0 ? (new AudioAttributes.Builder().build())
+                    : mAudioAttributes[0];
+        }
+
+        /**
+         * Checks if a {@link AudioAttributes} is supported by this product strategy.
+         * @param {@link AudioAttributes} to check upon support
+         * @return true if the {@link AudioAttributes} follows this product strategy,
+                   false otherwise.
+         */
+        public boolean supportsAttributes(@NonNull AudioAttributes attributes) {
+            for (final AudioAttributes refAa : mAudioAttributes) {
+                if (refAa.equals(attributes) || attributesMatches(refAa, attributes)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean supportsStreamType(int streamType) {
+            return mLegacyStreamType == streamType;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mGroupId);
+            dest.writeInt(mLegacyStreamType);
+            dest.writeInt(mAudioAttributes.length);
+            for (AudioAttributes attributes : mAudioAttributes) {
+                attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/);
+            }
+        }
+
+        public static final Parcelable.Creator<AudioAttributesGroup> CREATOR =
+                new Parcelable.Creator<AudioAttributesGroup>() {
+                    @Override
+                    public AudioAttributesGroup createFromParcel(@NonNull Parcel in) {
+                        int groupId = in.readInt();
+                        int streamType = in.readInt();
+                        int nbAttributes = in.readInt();
+                        AudioAttributes[] aa = new AudioAttributes[nbAttributes];
+                        for (int index = 0; index < nbAttributes; index++) {
+                            aa[index] = AudioAttributes.CREATOR.createFromParcel(in);
+                        }
+                        return new AudioAttributesGroup(groupId, streamType, aa);
+                    }
+
+                    @Override
+                    public @NonNull AudioAttributesGroup[] newArray(int size) {
+                        return new AudioAttributesGroup[size];
+                    }
+                };
+
+
+        @Override
+        public @NonNull String toString() {
+            StringBuilder s = new StringBuilder();
+            s.append("\n    Legacy Stream Type: ");
+            s.append(Integer.toString(mLegacyStreamType));
+            s.append(" Group Id: ");
+            s.append(Integer.toString(mGroupId));
+
+            for (AudioAttributes attribute : mAudioAttributes) {
+                s.append("\n    -");
+                s.append(attribute.toString());
+            }
+            return s.toString();
+        }
+    }
+}
diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java
index 64d283f..40c7166 100644
--- a/media/java/android/media/session/ControllerLink.java
+++ b/media/java/android/media/session/ControllerLink.java
@@ -493,6 +493,22 @@
     }
 
     /**
+     * Tell system that a controller requests changing the playback speed.
+     *
+     * @param packageName the package name of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param speed the playback speed
+     */
+    void setPlaybackSpeed(@NonNull String packageName, @NonNull ControllerCallbackLink caller,
+            float speed) {
+        try {
+            mISessionController.setPlaybackSpeed(packageName, caller, speed);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Tell system that a controller sends a custom action.
      *
      * @param packageName the package name of the controller
@@ -759,6 +775,11 @@
                 @NonNull Rating rating) {
         }
 
+        /** Stub method for ISessionController.setPlaybackSpeed */
+        public void setPlaybackSpeed(@NonNull String packageName,
+                @NonNull ControllerCallbackLink caller, float speed) {
+        }
+
         /** Stub method for ISessionController.sendCustomAction */
         public void sendCustomAction(@NonNull String packageName,
                 @NonNull ControllerCallbackLink caller, @NonNull String action,
@@ -953,6 +974,12 @@
         }
 
         @Override
+        public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+                float speed) {
+            mControllerStub.setPlaybackSpeed(packageName, caller, speed);
+        }
+
+        @Override
         public void sendCustomAction(String packageName, ControllerCallbackLink caller,
                 String action, Bundle args) {
             mControllerStub.sendCustomAction(packageName, caller, action, args);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9b86bfc..cd33c04 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -60,6 +60,8 @@
             long pos);
     void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             in Rating rating);
+    void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, float speed);
     void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             String action, in Bundle args);
 
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e697c65..3e7b4fb 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -76,6 +76,7 @@
     void rewind(String packageName, in ControllerCallbackLink caller);
     void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
     void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
+    void setPlaybackSpeed(String packageName, in ControllerCallbackLink caller, float speed);
     void sendCustomAction(String packageName, in ControllerCallbackLink caller,
             String action, in Bundle args);
     MediaMetadata getMetadata();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 6e2c8c5..9e4199c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -865,6 +865,19 @@
         }
 
         /**
+         * Set the playback speed.
+         *
+         * @param speed The playback speed
+         */
+        public void setPlaybackSpeed(float speed) {
+            try {
+                mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed);
+            } catch (RuntimeException e) {
+                Log.wtf(TAG, "Error calling setPlaybackSpeed.", e);
+            }
+        }
+
+        /**
          * Send a custom action back for the {@link MediaSession} to perform.
          *
          * @param customAction The action to perform.
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1b9ebda..8ab893b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -681,6 +681,19 @@
         }
 
         /**
+         * Override to handle the playback speed change.
+         * To update the new playback speed, create a new {@link PlaybackState} by using {@link
+         * PlaybackState.Builder#setState(int, long, float)}, and set it with
+         * {@link #setPlaybackState(PlaybackState)}.
+         *
+         * @param speed the playback speed
+         * @see #setPlaybackState(PlaybackState)
+         * @see PlaybackState.Builder#setState(int, long, float)
+         */
+        public void onSetPlaybackSpeed(float speed) {
+        }
+
+        /**
          * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
          * performed.
          *
diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java
index e19bdbc..266bf32 100644
--- a/media/java/android/media/session/MediaSessionEngine.java
+++ b/media/java/android/media/session/MediaSessionEngine.java
@@ -538,6 +538,10 @@
         postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
     }
 
+    void dispatchSetPlaybackSpeed(RemoteUserInfo caller, float speed) {
+        postToCallback(caller, CallbackMessageHandler.MSG_SET_PLAYBACK_SPEED, speed, null);
+    }
+
     void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
         postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
     }
@@ -871,6 +875,17 @@
         }
 
         /**
+         * Override to handle the playback speed change.
+         *
+         * @param speed the playback speed
+         */
+        public void onSetPlaybackSpeed(float speed) {
+            if (mCallback != null) {
+                mCallback.onSetPlaybackSpeed(speed);
+            }
+        }
+
+        /**
          * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
          * performed.
          *
@@ -1092,10 +1107,11 @@
         private static final int MSG_REWIND = 17;
         private static final int MSG_SEEK_TO = 18;
         private static final int MSG_RATE = 19;
-        private static final int MSG_CUSTOM_ACTION = 20;
-        private static final int MSG_ADJUST_VOLUME = 21;
-        private static final int MSG_SET_VOLUME = 22;
-        private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
+        private static final int MSG_SET_PLAYBACK_SPEED = 20;
+        private static final int MSG_CUSTOM_ACTION = 21;
+        private static final int MSG_ADJUST_VOLUME = 22;
+        private static final int MSG_SET_VOLUME = 23;
+        private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 24;
 
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         CallbackWrapper mCallbackWrapper;
@@ -1186,6 +1202,9 @@
                 case MSG_RATE:
                     mCallbackWrapper.onSetRating((Rating) obj);
                     break;
+                case MSG_SET_PLAYBACK_SPEED:
+                    mCallbackWrapper.onSetPlaybackSpeed((Float) obj);
+                    break;
                 case MSG_CUSTOM_ACTION:
                     mCallbackWrapper.onCustomAction((String) obj, msg.getData());
                     break;
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
index f59a69d..f9fa45a 100644
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -462,6 +462,25 @@
     }
 
     /**
+     * Notify session that a controller requests changing playback speed.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param speed the playback speed
+     */
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void notifySetPlaybackSpeed(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, float speed) {
+        try {
+            mISessionCallback.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Notify session that a controller sends a custom action.
      *
      * @param packageName the package name of the controller
@@ -871,6 +890,23 @@
             }
         }
 
+        @Override
+        public void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, float speed) {
+            ensureMediaControlPermission();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchSetPlaybackSpeed(
+                            createRemoteUserInfo(packageName, pid, uid), speed);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void notifyCustomAction(String packageName, int pid, int uid,
                 ControllerCallbackLink caller, String action, Bundle args) {
             ensureMediaControlPermission();
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index d069bd2..58317ed 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -16,7 +16,7 @@
     name: "libamidi",
 
     srcs: [
-        "midi.cpp",
+        "amidi.cpp",
         ":IMidiDeviceServer.aidl",
     ],
 
@@ -48,10 +48,10 @@
 
     from: "include",
 
-    to: "amidi",
+    to: "",
 
-    srcs: ["include/midi.h"],
-    license: "include/NOTICE",
+    srcs: ["include/amidi/AMidi.h"],
+    license: "include/amidi/NOTICE",
 }
 
 ndk_library {
diff --git a/media/native/midi/midi.cpp b/media/native/midi/amidi.cpp
similarity index 99%
rename from media/native/midi/midi.cpp
rename to media/native/midi/amidi.cpp
index a5bdba8..1e9a194 100644
--- a/media/native/midi/midi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -28,8 +28,8 @@
 #include "android/media/midi/BpMidiDeviceServer.h"
 #include "media/MidiDeviceInfo.h"
 
-#include "include/midi.h"
-#include "midi_internal.h"
+#include "include/amidi/AMidi.h"
+#include "amidi_internal.h"
 
 using namespace android::media::midi;
 
diff --git a/media/native/midi/midi_internal.h b/media/native/midi/amidi_internal.h
similarity index 93%
rename from media/native/midi/midi_internal.h
rename to media/native/midi/amidi_internal.h
index cb3ecce..fce8596 100644
--- a/media/native/midi/midi_internal.h
+++ b/media/native/midi/amidi_internal.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEDIA_MIDI_INTERNAL_H_
-#define ANDROID_MEDIA_MIDI_INTERNAL_H_
+#ifndef ANDROID_MEDIA_AMIDI_INTERNAL_H_
+#define ANDROID_MEDIA_AMIDI_INTERNAL_H_
 
 #include <jni.h>
 
@@ -38,4 +38,4 @@
     AMidiDeviceInfo deviceInfo; /* Attributes of the device. */
 };
 
-#endif // ANDROID_MEDIA_MIDI_INTERNAL_H_
+#endif // ANDROID_MEDIA_AMIDI_INTERNAL_H_
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/amidi/AMidi.h
similarity index 79%
rename from media/native/midi/include/midi.h
rename to media/native/midi/include/amidi/AMidi.h
index 755d09f..0d60b0d 100644
--- a/media/native/midi/include/midi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEDIA_MIDI_H_
-#define ANDROID_MEDIA_MIDI_H_
+#ifndef ANDROID_MEDIA_AMIDI_H_
+#define ANDROID_MEDIA_AMIDI_H_
 
 #include <stdarg.h>
 #include <stdint.h>
@@ -66,9 +66,9 @@
  * @param outDevicePtrPtr  Points to the pointer to receive the AMidiDevice
  *
  * @return AMEDIA_OK on success, or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - the midiDeviceObj
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the midiDeviceObj
  *    is null or already connected to a native AMidiDevice
-  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
+  *  @see AMEDIA_ERROR_UNKNOWN - an unknown error occurred.
  */
 media_status_t AMIDI_API AMidiDevice_fromJava(
         JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
@@ -80,13 +80,10 @@
  *
  * @return AMEDIA_OK on success,
  * or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER}
- *  - the device parameter is NULL.
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
- *  - the device is not consistent with the associated Java MidiDevice.
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
- *  - the JNI interface initialization to the associated java MidiDevice failed.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the device is not consistent with the associated Java MidiDevice.
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the JNI interface initialization to the associated java MidiDevice failed.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
 
@@ -100,9 +97,8 @@
  *  AMIDI_DEVICE_TYPE_VIRTUAL
  *  AMIDI_DEVICE_TYPE_BLUETOOTH
  * or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown error.
  */
 int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -113,9 +109,8 @@
  *
  * @return If successful, returns the number of MIDI input (sending) ports available on the
  * device. If an error occurs, returns a negative value indicating the error:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -126,9 +121,8 @@
  *
  * @return If successful, returns the number of MIDI output (receiving) ports available on the
  * device. If an error occurs, returns a negative value indicating the error:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -146,7 +140,7 @@
  * @param outOutputPortPtr Receives the native API port identifier of the opened port.
  *
  * @return AMEDIA_OK, or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
                              AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
@@ -174,7 +168,7 @@
  *  (the current value of the running Java Virtual Machine's high-resolution time source,
  *  in nanoseconds)
  * @return the number of messages received (either 0 or 1), or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
          uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
@@ -193,7 +187,7 @@
  * @param outInputPortPtr Receives the native API port identifier of the opened port.
  *
  * @return AMEDIA_OK, or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
                             AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
@@ -206,8 +200,7 @@
  * @param numBytes     Specifies the number of bytes to write.
  *
  * @return The number of bytes sent, which could be less than specified or a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL, the specified buffer was NULL.
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
                    size_t numBytes) __INTRODUCED_IN(29);
@@ -221,8 +214,7 @@
  * @param timestamp    The CLOCK_MONOTONIC time in nanoseconds to associate with the sent data.
  *
  * @return The number of bytes sent, which could be less than specified or a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL, the specified buffer was NULL.
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
         const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
@@ -233,10 +225,9 @@
  *
  * @param inputPort The identifier of the port to send the flush command to.
  *
- * @returns @see AMEDIA_OK {@link AMEDIA_OK} if successful, otherwise a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL
- * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
+ * @returns @see AMEDIA_OK if successful, otherwise a negative error code:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL
+ * @see AMEDIA_ERROR_UNSUPPORTED - The FLUSH command couldn't
  * be sent.
  */
 media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
@@ -252,4 +243,4 @@
 }
 #endif
 
-#endif /* ANDROID_MEDIA_MIDI_H_ */
+#endif /* ANDROID_MEDIA_AMIDI_H_ */
diff --git a/media/native/midi/include/NOTICE b/media/native/midi/include/amidi/NOTICE
similarity index 100%
rename from media/native/midi/include/NOTICE
rename to media/native/midi/include/amidi/NOTICE
diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt
index 7be6e73..bfb0b27 100644
--- a/media/proto/jarjar-rules.txt
+++ b/media/proto/jarjar-rules.txt
@@ -1,2 +1,2 @@
-rule com.google.protobuf.** com.android.framework.protobuf.@1
+rule com.google.protobuf.** com.android.media.protobuf.@1
 
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 730c409..a3db2d6 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -146,6 +146,7 @@
     AHardwareBuffer_getNativeHandle; # introduced=26
     AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock; # introduced=26
+    AHardwareBuffer_lockPlanes; # introduced=29
     AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
     AHardwareBuffer_release; # introduced=26
     AHardwareBuffer_sendHandleToUnixSocket; # introduced=26
diff --git a/packages/BackupRestoreConfirmation/Android.bp b/packages/BackupRestoreConfirmation/Android.bp
new file mode 100644
index 0000000..b0222da
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2011 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.
+//
+
+android_app {
+    name: "BackupRestoreConfirmation",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk
deleted file mode 100644
index 532d272..0000000
--- a/packages/BackupRestoreConfirmation/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2011 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := BackupRestoreConfirmation
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
index 7acdfa1..a71977f 100644
--- a/packages/CaptivePortalLogin/Android.bp
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -18,7 +18,7 @@
     name: "CaptivePortalLogin",
     srcs: ["src/**/*.java"],
     sdk_version: "system_current",
-    certificate: "platform",
+    certificate: "networkstack",
     static_libs: [
         "androidx.legacy_legacy-support-v4",
         "metrics-constants-protos",
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index e15dca0..0894ee5 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -23,8 +23,8 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
 
     <application android:label="@string/app_name"
                  android:usesCleartextTraffic="true"
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index ce627ce..a288d010 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -175,6 +175,7 @@
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
         webSettings.setDisplayZoomControls(false);
+        webSettings.setDomStorageEnabled(true);
         mWebViewClient = new MyWebViewClient();
         webview.setWebViewClient(mWebViewClient);
         webview.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
index ca343d1..8077431 100644
--- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
+++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
@@ -24,13 +24,17 @@
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.os.IBinder;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.GestureDetector;
@@ -91,12 +95,28 @@
     private static int sSettleOpenPercentage;
     private static int sSettleClosePercentage;
 
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+                mCarNotificationListener.registerAsSystemService(mContext,
+                        mCarUxRestrictionManagerWrapper,
+                        mClickHandlerFactory);
+                inflateNotificationContent();
+            }
+        }
+    };
+
     /**
      * Inits the window that hosts the notifications and establishes the connections
      * to the car related services.
      */
     @Override
     public void start() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
         sSettleOpenPercentage = mContext.getResources().getInteger(
                 R.integer.notification_settle_open_percentage);
         sSettleClosePercentage = mContext.getResources().getInteger(
@@ -117,8 +137,7 @@
                         closeCarNotifications(DEFAULT_FLING_VELOCITY);
                     }
                 });
-        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
-                mClickHandlerFactory);
+
         mCar = Car.createCar(mContext, mCarConnectionListener);
         mCar.connect();
         NotificationGestureListener gestureListener = new NotificationGestureListener();
@@ -131,8 +150,6 @@
         mCarNotificationWindow
                 .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color));
 
-        inflateNotificationContent();
-
         WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 2dba1d5b..369bb9f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -221,30 +221,6 @@
         }
     }
 
-    @Override
-    public void destroy() {
-        mCarBatteryController.stopListening();
-        mConnectedDeviceSignalController.stopListening();
-        mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener);
-        mDrivingStateHelper.disconnectFromCarService();
-
-        if (mNavigationBarWindow != null) {
-            mWindowManager.removeViewImmediate(mNavigationBarWindow);
-            mNavigationBarView = null;
-        }
-
-        if (mLeftNavigationBarWindow != null) {
-            mWindowManager.removeViewImmediate(mLeftNavigationBarWindow);
-            mLeftNavigationBarView = null;
-        }
-
-        if (mRightNavigationBarWindow != null) {
-            mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
-            mRightNavigationBarView = null;
-        }
-        super.destroy();
-    }
-
 
     @Override
     protected void makeStatusBarView() {
@@ -536,29 +512,49 @@
     }
 
     @Override
+    public void setLockscreenUser(int newUserId) {
+        super.setLockscreenUser(newUserId);
+        // Try to dismiss the keyguard after every user switch.
+        dismissKeyguardWhenUserSwitcherNotDisplayed();
+    }
+
+    @Override
     public void onStateChanged(int newState) {
         super.onStateChanged(newState);
 
         startSwitchToGuestTimerIfDrivingOnKeyguard();
 
-        if (mFullscreenUserSwitcher == null) {
-            return; // Not using the full screen user switcher.
-        }
-
-        if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
-            if (!mFullscreenUserSwitcher.isVisible()) {
-                // Current execution path continues to set state after this, thus we deffer the
-                // dismissal to the next execution cycle.
-                postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
-            }
+        if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
+            hideUserSwitcher();
         } else {
+            dismissKeyguardWhenUserSwitcherNotDisplayed();
+        }
+    }
+
+    /** Makes the full screen user switcher visible, if applicable. */
+    public void showUserSwitcher() {
+        if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+            mFullscreenUserSwitcher.show(); // Makes the switcher visible.
+        }
+    }
+
+    private void hideUserSwitcher() {
+        if (mFullscreenUserSwitcher != null) {
             mFullscreenUserSwitcher.hide();
         }
     }
 
-    public void showUserSwitcher() {
-        if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
-            mFullscreenUserSwitcher.show(); // Makes the switcher visible.
+    // We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
+    private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
+        if (mFullscreenUserSwitcher == null) {
+            return; // Not using the full screen user switcher.
+        }
+
+        if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER
+                && !mFullscreenUserSwitcher.isVisible()) {
+            // Current execution path continues to set state after this, thus we deffer the
+            // dismissal to the next execution cycle.
+            postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
         }
     }
 
diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp
new file mode 100644
index 0000000..c1b0b2d
--- /dev/null
+++ b/packages/CarrierDefaultApp/Android.bp
@@ -0,0 +1,6 @@
+android_app {
+    name: "CarrierDefaultApp",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
deleted file mode 100644
index df88afd..0000000
--- a/packages/CarrierDefaultApp/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CarrierDefaultApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
-# This finds and builds the test apk as well, so a single make does both.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index f36b4aa..55c9361 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -106,6 +106,7 @@
         webSettings.setLoadWithOverviewMode(true);
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
+        webSettings.setDomStorageEnabled(true);
         mWebViewClient = new MyWebViewClient();
         mWebView.setWebViewClient(mWebViewClient);
         mWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/CarrierDefaultApp/tests/Android.mk b/packages/CarrierDefaultApp/tests/Android.mk
deleted file mode 100644
index 6ebb575..0000000
--- a/packages/CarrierDefaultApp/tests/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2016, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CERTIFICATE := platform
-
-# Include all makefiles in subdirectories
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-
-
-
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp
new file mode 100644
index 0000000..96144cf
--- /dev/null
+++ b/packages/CarrierDefaultApp/tests/unit/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2016, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "CarrierDefaultAppUnitTests",
+    certificate: "platform",
+    libs: [
+        "android.test.runner",
+        "telephony-common",
+        "android.test.base",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+    ],
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    instrumentation_for: "CarrierDefaultApp",
+}
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk
deleted file mode 100644
index 4c638811..0000000
--- a/packages/CarrierDefaultApp/tests/unit/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2016, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-LOCAL_CERTIFICATE := platform
-
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules mockito-target-minus-junit4
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CarrierDefaultAppUnitTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_INSTRUMENTATION_FOR := CarrierDefaultApp
-
-include $(BUILD_PACKAGE)
-
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
new file mode 100644
index 0000000..a379bfc
--- /dev/null
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+android_app {
+    name: "CompanionDeviceManager",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+}
diff --git a/packages/CompanionDeviceManager/Android.mk b/packages/CompanionDeviceManager/Android.mk
deleted file mode 100644
index 7ec6e11..0000000
--- a/packages/CompanionDeviceManager/Android.mk
+++ /dev/null
@@ -1,28 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CompanionDeviceManager
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
new file mode 100644
index 0000000..824cc69
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Koppel met &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Koppel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; met &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
new file mode 100644
index 0000000..5d89504
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"ከ&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ጋር አገናኝ"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ከ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; አገናኝ"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
new file mode 100644
index 0000000..9199986
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string>
+    <string name="chooser_title" msgid="4958797271463138976">"‏الربط باستخدام تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"‏ربط تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بجهاز &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
new file mode 100644
index 0000000..2bd6d7e
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ৰ সৈতে লিংক কৰক"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ৰ সৈতে &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; লিংক কৰক"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
new file mode 100644
index 0000000..c992cfd
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilə əlaqələndirin"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqini &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ilə əlaqələndirin"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..8a388a4
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Povežite sa aplikacijom &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; i uređaj &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
new file mode 100644
index 0000000..e1b8016
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Звяжыце з праграмай &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Звяжыце праграму &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; з прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
new file mode 100644
index 0000000..8748e20
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Свързване с(ъс) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Свържете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; с(ъс) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
new file mode 100644
index 0000000..a9fb9ff
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-এর সাথে লিঙ্ক করুন"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-এর সাথে &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; লিঙ্ক করুন"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
new file mode 100644
index 0000000..220553c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Povežite se s aplikacijom &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; s uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
new file mode 100644
index 0000000..0ad921a
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Aplicació Gestor de dispositius complementaris"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Enllaça amb &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Enllaça &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; amb &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
new file mode 100644
index 0000000..ebd9cb1
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Propojení s aplikací &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Propojení aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; se zařízením &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
new file mode 100644
index 0000000..7601e40
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Tilknyt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Knyt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
new file mode 100644
index 0000000..b58f2c5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Mit &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; verknüpfen"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mit &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; verknüpfen"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
new file mode 100644
index 0000000..2d56213
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Σύνδεση με &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Σύνδεση &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; με &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..91c7643
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Link with &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Link &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..91c7643
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Link with &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Link &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..91c7643
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Link with &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Link &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..91c7643
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Link with &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Link &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..e052e61
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎Companion Device Manager‎‏‎‎‏‎"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎Link with &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‎Link &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; with &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..0552ee7
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Vincular con &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Vincular &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; con &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
new file mode 100644
index 0000000..c9e218e
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Vincular con &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Vincular &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; con &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
new file mode 100644
index 0000000..1ac26f7
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Linkimine rakendusega &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Rakenduse &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; linkimine seadmega &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
new file mode 100644
index 0000000..26e1979
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Lotu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioarekin"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Lotu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; gailuarekin"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
new file mode 100644
index 0000000..b43fe29
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"‏پیوند با &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"‏پیوند &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; با &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
new file mode 100644
index 0000000..fbc18f6
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Linkitä &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Linkitä &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ja &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..05e3402
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Lier à &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Lier &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
new file mode 100644
index 0000000..881d6aa
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Associer à l\'application &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Associer l\'application &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à l\'appareil &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
new file mode 100644
index 0000000..6f85022
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Vincular con &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Vincular &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; con &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
new file mode 100644
index 0000000..5088507
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સાથે લિંક કરો"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; સાથે લિંક કરો"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
new file mode 100644
index 0000000..4afcb63
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; से लिंक करें"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; से लिंक करें"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
new file mode 100644
index 0000000..7b71939
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Povežite s aplikacijom &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; s uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
new file mode 100644
index 0000000..19920b2
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Összekapcsolás a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazással"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás és a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; eszköz összekapcsolása"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
new file mode 100644
index 0000000..8dee4a3
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածի հետ կապում"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածի կապում &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքի հետ"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
new file mode 100644
index 0000000..efd77fe
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Tautkan dengan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Tautkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dengan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
new file mode 100644
index 0000000..4bf9447
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Tengjast við &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Tengja &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; við &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
new file mode 100644
index 0000000..a602061
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gestione dispositivi companion"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Collega con &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Collega &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; con &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
new file mode 100644
index 0000000..b95fae5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"‏קישור אל &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"‏קישור &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; אל &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
new file mode 100644
index 0000000..8c39e70
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; とのリンク"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; と &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; とのリンク"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
new file mode 100644
index 0000000..6fa899a
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-თან მიბმა"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"მიაბით ერთმანეთს &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; და &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
new file mode 100644
index 0000000..18ab5e6
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасымен байланыстыру"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасымен және &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысымен байланыстыру"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
new file mode 100644
index 0000000..42efac4
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"កម្មវិធី​គ្រប់​គ្រង​ឧបករណ៍ដៃគូ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"ភ្ជាប់​ជាមួយ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"ភ្ជាប់​ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ជាមួយ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
new file mode 100644
index 0000000..e21bb02
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ನ ಜೊತೆಗೆ ಲಿಂಕ್ ಮಾಡಿ"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ನ ಜೊತೆಗೆ ಲಿಂಕ್ ಮಾಡಿ"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
new file mode 100644
index 0000000..17702f5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 연결"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;을(를) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;에 연결"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
new file mode 100644
index 0000000..63efea7
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосу менен байланыштыруу"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосун &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгү менен байланыштыруу"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
new file mode 100644
index 0000000..f637551
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"ເຊື່ອມຕໍ່ກັບ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"ເຊື່ອມຕໍ່ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ກັບ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
new file mode 100644
index 0000000..ddaa601
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Susieti su pr. &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Susieti programą &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; su &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; įrenginiu"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
new file mode 100644
index 0000000..ba8840c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Saistīšana ar lietotni &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; saistīšana ar ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
new file mode 100644
index 0000000..4c2e4117
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Поврзување со &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Поврзување на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; со &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
new file mode 100644
index 0000000..950e4e4
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് ലിങ്ക് ചെയ്യുക"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ലിങ്ക് ചെയ്യുക"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
new file mode 100644
index 0000000..5add54a
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-тай холбох"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-тай холбох"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
new file mode 100644
index 0000000..45348c0
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;strong&gt; सह लिंक करा"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ला &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; शी लिंक करा"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
new file mode 100644
index 0000000..5a50f15
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Paut dengan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Paut &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dengan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
new file mode 100644
index 0000000..3fba5ff
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ဖြင့် ချိတ်ဆက်ရန်&lt;strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ဖြင့် ချိတ်ဆက်ခြင်း &lt;strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
new file mode 100644
index 0000000..0b49f47
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Knytt til &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Knytt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
new file mode 100644
index 0000000..348104f
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सँग लिंक गर्नुहोस्"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; सँग &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई लिंक गर्नुहोस्"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
new file mode 100644
index 0000000..12177ca
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Koppelen met &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; met &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; koppelen"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
new file mode 100644
index 0000000..b5a9e1c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ସହ ଲିଙ୍କ୍ କରନ୍ତୁ"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ସହିତ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ଲିଙ୍କ୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
new file mode 100644
index 0000000..70a408f
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨਾਲ ਲਿੰਕ ਕਰੋ"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਨਾਲ ਲਿੰਕ ਕਰੋ"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
new file mode 100644
index 0000000..c622cedc
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Połącz z: &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Połącz: &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; z: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..b18c3ad
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Vincular a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Vincular &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..a64c544
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Associe à aplicação &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Associe a aplicação &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ao dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
new file mode 100644
index 0000000..b18c3ad
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Vincular a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Vincular &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
new file mode 100644
index 0000000..1c78602
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Conectați cu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Conectați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; cu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
new file mode 100644
index 0000000..74b9100
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Подключение к приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Подключение приложения &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; к устройству &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
new file mode 100644
index 0000000..89f4409
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; සමඟ සම්බන්ධ කරන්න"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; සමඟ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; සම්බන්ධ කරන්න"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
new file mode 100644
index 0000000..21b3b30
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Prepojenie s aplikáciou &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Prepojenie &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; so zariadením &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
new file mode 100644
index 0000000..5645fb1
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Povezava z aplikacijo &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Povezava aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; z napravo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
new file mode 100644
index 0000000..793554f
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Lidh me &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Lidh &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; me &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
new file mode 100644
index 0000000..eac6805
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Повежите са апликацијом &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Повежите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; и уређај &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
new file mode 100644
index 0000000..7d2ad85
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Länka med &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Länka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; till &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
new file mode 100644
index 0000000..8308bbd
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Unganisha na &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Ungansha &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
new file mode 100644
index 0000000..0511037
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸுடன் இணைத்தல்"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; சாதனத்துடன் இணைத்தல்"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
new file mode 100644
index 0000000..ebcc7f5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;తో లింక్ చేయండి"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;తో లింక్ చేయండి"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
new file mode 100644
index 0000000..3240794
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"ลิงก์กับ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"ลิงก์ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; กับ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
new file mode 100644
index 0000000..444b0d8
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"I-link sa &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"I-link ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; sa &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
new file mode 100644
index 0000000..9c20ed4
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasına bağlan"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazına bağla"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
new file mode 100644
index 0000000..bed8d4d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Налаштуйте зв’язок із додатком &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Зв’яжіть пристрій &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; з додатком &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
new file mode 100644
index 0000000..e9ad738
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"‏;lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&amp;gt&amp; سے لنک کریں"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; سے لنک کریں"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
new file mode 100644
index 0000000..c7d82ad
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ulash"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ilovasiga ulash"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
new file mode 100644
index 0000000..29e128c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Liên kết với &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Liên kết &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; với &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..8e962185
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"关联 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"将 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 关联到 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..dd055d0
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;連接"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"連結「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;和「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..2c46d59
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"與「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;連結"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"為「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;與&lt;strong&gt;&lt;/strong&gt; <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 建立連結"</string>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
new file mode 100644
index 0000000..80a24ff
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string>
+    <string name="chooser_title" msgid="4958797271463138976">"Xhuma ne-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="confirmation_title" msgid="5683126664999349196">"Xhuma ne-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; nge-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+</resources>
diff --git a/packages/DefaultContainerService/Android.bp b/packages/DefaultContainerService/Android.bp
new file mode 100644
index 0000000..d4ba6e8
--- /dev/null
+++ b/packages/DefaultContainerService/Android.bp
@@ -0,0 +1,8 @@
+android_app {
+    name: "DefaultContainerService",
+    srcs: ["**/*.java"],
+    platform_apis: true,
+    jni_libs: ["libdefcontainer_jni"],
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
deleted file mode 100644
index 10c35c0..0000000
--- a/packages/DefaultContainerService/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := DefaultContainerService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
diff --git a/packages/FakeOemFeatures/Android.mk b/packages/DynamicAndroidInstallationService/Android.mk
similarity index 61%
rename from packages/FakeOemFeatures/Android.mk
rename to packages/DynamicAndroidInstallationService/Android.mk
index 43de8e5..13d96ac 100644
--- a/packages/FakeOemFeatures/Android.mk
+++ b/packages/DynamicAndroidInstallationService/Android.mk
@@ -4,13 +4,16 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
 
-LOCAL_PACKAGE_NAME := FakeOemFeatures
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PACKAGE_NAME := DynamicAndroidInstallationService
 LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-include $(BUILD_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_PACKAGE)
diff --git a/packages/DynamicAndroidInstallationService/AndroidManifest.xml b/packages/DynamicAndroidInstallationService/AndroidManifest.xml
new file mode 100644
index 0000000..32acad4
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.dynandroid"
+        android:sharedUserId="android.uid.system">
+
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.MANAGE_DYNAMIC_ANDROID" />
+    <uses-permission android:name="android.permission.REBOOT" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+    <application
+        android:allowBackup="false"
+        android:label="@string/app_name">
+
+        <service
+            android:name=".DynamicAndroidInstallationService"
+            android:enabled="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DYNAMIC_ANDROID"
+            android:process=":dynandroid">
+            <intent-filter>
+                <action android:name="android.content.action.NOTIFY_IF_IN_USE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+
+        <activity android:name=".VerificationActivity"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DYNAMIC_ANDROID"
+            android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
+            android:process=":dynandroid">
+            <intent-filter>
+                <action android:name="android.content.action.START_INSTALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <receiver
+            android:name=".BootCompletedReceiver"
+            android:enabled="true"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2 b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2
diff --git a/packages/DynamicAndroidInstallationService/NOTICE b/packages/DynamicAndroidInstallationService/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml
new file mode 100644
index 0000000..acf1567
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z"
+      android:fillColor="#4285F4"/>
+</vector>
diff --git a/packages/DynamicAndroidInstallationService/res/values/strings.xml b/packages/DynamicAndroidInstallationService/res/values/strings.xml
new file mode 100644
index 0000000..221e1d7
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- application name [CHAR LIMIT=32] -->
+    <string name="app_name">AndroidOnTap Installer</string>
+
+    <!-- notification channel name [CHAR LIMIT=32] -->
+    <string name="notification_channel_name">AndroidOnTap Installer</string>
+
+    <!-- password page title [CHAR LIMIT=32] -->
+    <string name="keyguard_title">AndroidOnTap Installer</string>
+
+    <!-- password page description [CHAR LIMIT=128] -->
+    <string name="keyguard_description">Please enter your password and continue to AndroidOnTap installation</string>
+
+    <!-- Displayed on notification: DynAndroid installation is completed [CHAR LIMIT=128] -->
+    <string name="notification_install_completed">Installation is completed, you can reboot into the new installed system now.</string>
+    <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] -->
+    <string name="notification_install_inprogress">Installation is in progress.</string>
+    <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] -->
+    <string name="notification_install_failed">Installation Failed.</string>
+    <!-- Displayed on notification: We are running in AndroidOnTap [CHAR LIMIT=128] -->
+    <string name="notification_dynandroid_in_use">We are running in AndroidOnTap.</string>
+
+    <!-- Action on notification: Cancel installation [CHAR LIMIT=16] -->
+    <string name="notification_action_cancel">Cancel</string>
+    <!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
+    <string name="notification_action_discard">Discard</string>
+    <!-- Action on notification: Uninstall AndroidOnTap [CHAR LIMIT=16] -->
+    <string name="notification_action_uninstall">Uninstall</string>
+    <!-- Action on notification: Reboot to AndroidOnTap [CHAR LIMIT=16] -->
+    <string name="notification_action_reboot_to_dynandroid">Reboot</string>
+
+</resources>
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
new file mode 100644
index 0000000..dd1be89
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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.dynandroid;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.util.Log;
+
+
+/**
+ * A BoardcastReceiver waiting for ACTION_BOOT_COMPLETED and ask
+ * the service to display a notification if we are currently running
+ * in DynamicAndroid.
+ */
+public class BootCompletedReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "BootCompletedReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        Log.d(TAG, "Broadcast received: " + action);
+
+        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            Intent startServiceIntent = new Intent(
+                    context, DynamicAndroidInstallationService.class);
+
+            startServiceIntent.setAction(DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE);
+            context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
+        }
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
new file mode 100644
index 0000000..7755cbc
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2019 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.dynandroid;
+
+import static android.content.DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE;
+import static android.content.DynamicAndroidClient.ACTION_START_INSTALL;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_EXCEPTION;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_INVALID_URL;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_IO;
+import static android.content.DynamicAndroidClient.CAUSE_INSTALL_CANCELLED;
+import static android.content.DynamicAndroidClient.CAUSE_INSTALL_COMPLETED;
+import static android.content.DynamicAndroidClient.CAUSE_NOT_SPECIFIED;
+import static android.content.DynamicAndroidClient.STATUS_IN_PROGRESS;
+import static android.content.DynamicAndroidClient.STATUS_IN_USE;
+import static android.content.DynamicAndroidClient.STATUS_NOT_STARTED;
+import static android.content.DynamicAndroidClient.STATUS_READY;
+import static android.os.AsyncTask.Status.FINISHED;
+import static android.os.AsyncTask.Status.PENDING;
+import static android.os.AsyncTask.Status.RUNNING;
+
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_INVALID_URL;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_IO;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_OK;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.DynamicAndroidManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * This class is the service in charge of DynamicAndroid installation.
+ * It also posts status to notification bar and wait for user's
+ * cancel and confirm commnands.
+ */
+public class DynamicAndroidInstallationService extends Service
+        implements InstallationAsyncTask.InstallStatusListener {
+
+    private static final String TAG = "DynAndroidInstallationService";
+
+    /*
+     * Intent actions
+     */
+    private static final String ACTION_CANCEL_INSTALL =
+            "com.android.dynandroid.ACTION_CANCEL_INSTALL";
+    private static final String ACTION_REBOOT_TO_DYN_ANDROID =
+            "com.android.dynandroid.ACTION_REBOOT_TO_DYN_ANDROID";
+    private static final String ACTION_REBOOT_TO_NORMAL =
+            "com.android.dynandroid.ACTION_REBOOT_TO_NORMAL";
+
+    /*
+     * For notification
+     */
+    private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynandroid";
+    private static final int NOTIFICATION_ID = 1;
+
+    /*
+     * IPC
+     */
+    /** Keeps track of all current registered clients. */
+    ArrayList<Messenger> mClients = new ArrayList<>();
+
+    /** Handler of incoming messages from clients. */
+    final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+    static class IncomingHandler extends Handler {
+        private final WeakReference<DynamicAndroidInstallationService> mWeakService;
+
+        IncomingHandler(DynamicAndroidInstallationService service) {
+            mWeakService = new WeakReference<>(service);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DynamicAndroidInstallationService service = mWeakService.get();
+
+            if (service != null) {
+                service.handleMessage(msg);
+            }
+        }
+    }
+
+    private DynamicAndroidManager mDynAndroid;
+    private NotificationManager mNM;
+
+    private long mSystemSize;
+    private long mInstalledSize;
+    private boolean mJustCancelledByUser;
+
+    private PendingIntent mPiCancel;
+    private PendingIntent mPiRebootToDynamicAndroid;
+    private PendingIntent mPiUninstallAndReboot;
+
+    private InstallationAsyncTask mInstallTask;
+
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        prepareNotification();
+
+        mDynAndroid = (DynamicAndroidManager) getSystemService(Context.DYNAMIC_ANDROID_SERVICE);
+    }
+
+    @Override
+    public void onDestroy() {
+        // Cancel the persistent notification.
+        mNM.cancel(NOTIFICATION_ID);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String action = intent.getAction();
+
+        Log.d(TAG, "onStartCommand(): action=" + action);
+
+        if (ACTION_START_INSTALL.equals(action)) {
+            executeInstallCommand(intent);
+        } else if (ACTION_CANCEL_INSTALL.equals(action)) {
+            executeCancelCommand();
+        } else if (ACTION_REBOOT_TO_DYN_ANDROID.equals(action)) {
+            executeRebootToDynAndroidCommand();
+        } else if (ACTION_REBOOT_TO_NORMAL.equals(action)) {
+            executeRebootToNormalCommand();
+        } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
+            executeNotifyIfInUseCommand();
+        }
+
+        return Service.START_NOT_STICKY;
+    }
+
+    @Override
+    public void onProgressUpdate(long installedSize) {
+        mInstalledSize = installedSize;
+        postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED);
+    }
+
+    @Override
+    public void onResult(int result) {
+        if (result == RESULT_OK) {
+            postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED);
+            return;
+        }
+
+        // if it's not successful, reset the task and stop self.
+        resetTaskAndStop();
+
+        switch (result) {
+            case RESULT_ERROR_IO:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO);
+                break;
+
+            case RESULT_ERROR_INVALID_URL:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL);
+                break;
+
+            case RESULT_ERROR_EXCEPTION:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION);
+                break;
+        }
+    }
+
+    @Override
+    public void onCancelled() {
+        resetTaskAndStop();
+        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+    }
+
+    private void executeInstallCommand(Intent intent) {
+        if (!verifyRequest(intent)) {
+            Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
+            return;
+        }
+
+        if (mInstallTask != null) {
+            Log.e(TAG, "There is already an install task running");
+            return;
+        }
+
+        if (isInDynamicAndroid()) {
+            Log.e(TAG, "We are already running in DynamicAndroid");
+            return;
+        }
+
+        String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL);
+        mSystemSize = intent.getLongExtra(DynamicAndroidClient.KEY_SYSTEM_SIZE, 0);
+        long userdata = intent.getLongExtra(DynamicAndroidClient.KEY_USERDATA_SIZE, 0);
+
+        mInstallTask = new InstallationAsyncTask(url, mSystemSize, userdata, mDynAndroid, this);
+        mInstallTask.execute();
+
+        // start fore ground
+        startForeground(NOTIFICATION_ID,
+                buildNotification(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED));
+    }
+
+    private void executeCancelCommand() {
+        if (mInstallTask == null || mInstallTask.getStatus() == PENDING) {
+            Log.e(TAG, "Cancel command triggered, but there is no task running");
+            mNM.cancel(NOTIFICATION_ID);
+
+            return;
+        }
+
+        mJustCancelledByUser = true;
+
+        if (mInstallTask.cancel(false)) {
+            // Will cleanup and post status in onCancelled()
+            Log.d(TAG, "Cancel request filed successfully");
+        } else {
+            Log.d(TAG, "Requested cancel, completed task will be discarded");
+
+            resetTaskAndStop();
+            postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+        }
+
+    }
+
+    private void executeRebootToDynAndroidCommand() {
+        if (mInstallTask == null || mInstallTask.getStatus() != FINISHED) {
+            Log.e(TAG, "Trying to reboot to DynamicAndroid, but there is no complete installation");
+            return;
+        }
+
+        if (!mInstallTask.commit()) {
+            // TODO: b/123673280 better UI response
+            Log.e(TAG, "Failed to commit installation because of native runtime error.");
+            mNM.cancel(NOTIFICATION_ID);
+
+            return;
+        }
+
+        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        if (powerManager != null) {
+            powerManager.reboot("dynandroid");
+        }
+    }
+
+    private void executeRebootToNormalCommand() {
+        mDynAndroid.remove();
+
+        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        if (powerManager != null) {
+            powerManager.reboot(null);
+        }
+    }
+
+    private void executeNotifyIfInUseCommand() {
+        if (isInDynamicAndroid()) {
+            startForeground(NOTIFICATION_ID,
+                    buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+        }
+    }
+
+    private void resetTaskAndStop() {
+        mInstallTask = null;
+
+        stopForeground(true);
+
+        // stop self, but this service is not destroyed yet if it's still bound
+        stopSelf();
+    }
+
+    private void prepareNotification() {
+        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                getString(R.string.notification_channel_name),
+                NotificationManager.IMPORTANCE_LOW);
+
+        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+        if (mNM != null) {
+            mNM.createNotificationChannel(chan);
+        }
+
+        Intent intentCancel = new Intent(this, DynamicAndroidInstallationService.class);
+        intentCancel.setAction(ACTION_CANCEL_INSTALL);
+        mPiCancel = PendingIntent.getService(this, 0, intentCancel, 0);
+
+        Intent intentRebootToDyn = new Intent(this, DynamicAndroidInstallationService.class);
+        intentRebootToDyn.setAction(ACTION_REBOOT_TO_DYN_ANDROID);
+        mPiRebootToDynamicAndroid = PendingIntent.getService(this, 0, intentRebootToDyn, 0);
+
+        Intent intentUninstallAndReboot = new Intent(this, DynamicAndroidInstallationService.class);
+        intentUninstallAndReboot.setAction(ACTION_REBOOT_TO_NORMAL);
+        mPiUninstallAndReboot = PendingIntent.getService(this, 0, intentUninstallAndReboot, 0);
+    }
+
+    private Notification buildNotification(int status, int cause) {
+        Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
+                .setProgress(0, 0, false);
+
+        switch (status) {
+            case STATUS_IN_PROGRESS:
+                builder.setContentText(getString(R.string.notification_install_inprogress));
+
+                int max = (int) Math.max(mSystemSize >> 20, 1);
+                int progress = (int) mInstalledSize >> 20;
+
+                builder.setProgress(max, progress, false);
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_cancel),
+                        mPiCancel).build());
+
+                break;
+
+            case STATUS_READY:
+                builder.setContentText(getString(R.string.notification_install_completed));
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_reboot_to_dynandroid),
+                        mPiRebootToDynamicAndroid).build());
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_cancel),
+                        mPiCancel).build());
+
+                break;
+
+            case STATUS_IN_USE:
+                builder.setContentText(getString(R.string.notification_dynandroid_in_use));
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_uninstall),
+                        mPiUninstallAndReboot).build());
+
+                break;
+
+            case STATUS_NOT_STARTED:
+                if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
+                    builder.setContentText(getString(R.string.notification_install_failed));
+                } else {
+                    // no need to notify the user if the task is not started, or cancelled.
+                }
+                break;
+
+            default:
+                throw new IllegalStateException("status is invalid");
+        }
+
+        return builder.build();
+    }
+
+    private boolean verifyRequest(Intent intent) {
+        String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL);
+
+        return VerificationActivity.isVerified(url);
+    }
+
+    private void postStatus(int status, int cause) {
+        Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
+
+        boolean notifyOnNotificationBar = true;
+
+        if (status == STATUS_NOT_STARTED
+                && cause == CAUSE_INSTALL_CANCELLED
+                && mJustCancelledByUser) {
+            // if task is cancelled by user, do not notify them
+            notifyOnNotificationBar = false;
+            mJustCancelledByUser = false;
+        }
+
+        if (notifyOnNotificationBar) {
+            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
+        }
+
+        for (int i = mClients.size() - 1; i >= 0; i--) {
+            try {
+                notifyOneClient(mClients.get(i), status, cause);
+            } catch (RemoteException e) {
+                mClients.remove(i);
+            }
+        }
+    }
+
+    private void notifyOneClient(Messenger client, int status, int cause) throws RemoteException {
+        Bundle bundle = new Bundle();
+
+        bundle.putLong(DynamicAndroidClient.KEY_INSTALLED_SIZE, mInstalledSize);
+
+        client.send(Message.obtain(null,
+                  DynamicAndroidClient.MSG_POST_STATUS, status, cause, bundle));
+    }
+
+    private int getStatus() {
+        if (isInDynamicAndroid()) {
+            return STATUS_IN_USE;
+
+        } else if (mInstallTask == null) {
+            return STATUS_NOT_STARTED;
+
+        }
+
+        switch (mInstallTask.getStatus()) {
+            case PENDING:
+                return STATUS_NOT_STARTED;
+
+            case RUNNING:
+                return STATUS_IN_PROGRESS;
+
+            case FINISHED:
+                int result = mInstallTask.getResult();
+
+                if (result == RESULT_OK) {
+                    return STATUS_READY;
+                } else {
+                    throw new IllegalStateException("A failed InstallationTask is not reset");
+                }
+
+            default:
+                return STATUS_NOT_STARTED;
+        }
+    }
+
+    private boolean isInDynamicAndroid() {
+        return mDynAndroid.isInUse();
+    }
+
+    void handleMessage(Message msg) {
+        switch (msg.what) {
+            case DynamicAndroidClient.MSG_REGISTER_LISTENER:
+                try {
+                    Messenger client = msg.replyTo;
+
+                    int status = getStatus();
+
+                    // tell just registered client my status, but do not specify cause
+                    notifyOneClient(client, status, CAUSE_NOT_SPECIFIED);
+
+                    mClients.add(client);
+                } catch (RemoteException e) {
+                    // do nothing if we cannot send update to the client
+                    e.printStackTrace();
+                }
+
+                break;
+            case DynamicAndroidClient.MSG_UNREGISTER_LISTENER:
+                mClients.remove(msg.replyTo);
+                break;
+            default:
+                // do nothing
+        }
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
new file mode 100644
index 0000000..3c759e9
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2019 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.dynandroid;
+
+import android.os.AsyncTask;
+import android.os.DynamicAndroidManager;
+import android.util.Log;
+import android.webkit.URLUtil;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.zip.GZIPInputStream;
+
+
+class InstallationAsyncTask extends AsyncTask<String, Long, Integer> {
+
+    private static final String TAG = "InstallationAsyncTask";
+
+    private static final int READ_BUFFER_SIZE = 1 << 19;
+
+    private class InvalidImageUrlException extends RuntimeException {
+        private InvalidImageUrlException(String message) {
+            super(message);
+        }
+    }
+
+
+    /** Not completed, including being cancelled */
+    static final int NO_RESULT = 0;
+    static final int RESULT_OK = 1;
+    static final int RESULT_ERROR_IO = 2;
+    static final int RESULT_ERROR_INVALID_URL = 3;
+    static final int RESULT_ERROR_EXCEPTION = 6;
+
+    interface InstallStatusListener {
+        void onProgressUpdate(long installedSize);
+        void onResult(int resultCode);
+        void onCancelled();
+    }
+
+    private final String mUrl;
+    private final long mSystemSize;
+    private final long mUserdataSize;
+    private final DynamicAndroidManager mDynamicAndroid;
+    private final InstallStatusListener mListener;
+    private DynamicAndroidManager.Session mInstallationSession;
+
+    private long mInstalledSize;
+    private long mReportedInstalledSize;
+    private int mResult = NO_RESULT;
+
+    private InputStream mStream;
+
+
+    InstallationAsyncTask(String url, long systemSize, long userdataSize,
+            DynamicAndroidManager dynAndroid, InstallStatusListener listener) {
+        mUrl = url;
+        mSystemSize = systemSize;
+        mUserdataSize = userdataSize;
+        mDynamicAndroid = dynAndroid;
+        mListener = listener;
+    }
+
+    @Override
+    protected void onPreExecute() {
+        mListener.onProgressUpdate(0);
+    }
+
+    @Override
+    protected Integer doInBackground(String... voids) {
+        Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
+
+        try {
+            // call start in background
+            mInstallationSession = mDynamicAndroid.startInstallation(mSystemSize, mUserdataSize);
+
+            if (mInstallationSession == null) {
+                Log.e(TAG, "Failed to start installation with requested size: "
+                        + (mSystemSize + mUserdataSize));
+
+                return RESULT_ERROR_IO;
+            }
+
+            initInputStream();
+
+            byte[] bytes = new byte[READ_BUFFER_SIZE];
+
+            int numBytesRead;
+            long minStepToReport = mSystemSize / 100;
+
+            Log.d(TAG, "Start installation loop");
+            while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+                if (isCancelled()) {
+                    break;
+                }
+
+                byte[] writeBuffer = numBytesRead == READ_BUFFER_SIZE
+                        ? bytes : Arrays.copyOf(bytes, numBytesRead);
+
+                if (!mInstallationSession.write(writeBuffer)) {
+                    throw new IOException("Failed write() to DynamicAndroid");
+                }
+
+                mInstalledSize += numBytesRead;
+
+                if (mInstalledSize > mReportedInstalledSize + minStepToReport) {
+                    publishProgress(mInstalledSize);
+                    mReportedInstalledSize = mInstalledSize;
+                }
+            }
+
+            return RESULT_OK;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return RESULT_ERROR_IO;
+
+        } catch (InvalidImageUrlException e) {
+            e.printStackTrace();
+            return RESULT_ERROR_INVALID_URL;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return RESULT_ERROR_EXCEPTION;
+
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    protected void onCancelled() {
+        Log.d(TAG, "onCancelled(), URL: " + mUrl);
+
+        close();
+
+        mListener.onCancelled();
+    }
+
+    @Override
+    protected void onPostExecute(Integer result) {
+        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+
+        close();
+
+        mResult = result;
+        mListener.onResult(mResult);
+    }
+
+    @Override
+    protected void onProgressUpdate(Long... values) {
+        long progress = values[0];
+        mListener.onProgressUpdate(progress);
+    }
+
+    private void initInputStream() throws IOException, InvalidImageUrlException {
+        if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) {
+            mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream()));
+        } else {
+            throw new InvalidImageUrlException(
+                    String.format(Locale.US, "Unsupported file source: %s", mUrl));
+        }
+    }
+
+    private void close() {
+        try {
+            if (mStream != null) {
+                mStream.close();
+                mStream = null;
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    int getResult() {
+        return mResult;
+    }
+
+    boolean commit() {
+        if (mInstallationSession == null) {
+            return false;
+        }
+
+        return mInstallationSession.commit();
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java
new file mode 100644
index 0000000..c18c4fe
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 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.dynandroid;
+
+import static android.content.DynamicAndroidClient.KEY_SYSTEM_SIZE;
+import static android.content.DynamicAndroidClient.KEY_SYSTEM_URL;
+import static android.content.DynamicAndroidClient.KEY_USERDATA_SIZE;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+
+/**
+ * This Activity starts KeyguardManager and ask the user to confirm
+ * before any installation request. If the device is not protected by
+ * a password, it approves the request by default.
+ */
+public class VerificationActivity extends Activity {
+
+    private static final String TAG = "VerificationActivity";
+
+    private static final int REQUEST_CODE = 1;
+
+    // For install request verification
+    private static String sVerifiedUrl;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+
+        if (km != null) {
+            String title = getString(R.string.keyguard_title);
+            String description = getString(R.string.keyguard_description);
+            Intent intent = km.createConfirmDeviceCredentialIntent(title, description);
+
+            if (intent == null) {
+                Log.d(TAG, "This device is not protected by a password/pin");
+                startInstallationService();
+                finish();
+            } else {
+                startActivityForResult(intent, REQUEST_CODE);
+            }
+        } else {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
+            startInstallationService();
+        }
+
+        finish();
+    }
+
+    private void startInstallationService() {
+        // retrieve data from calling intent
+        Intent callingIntent = getIntent();
+
+        String url = callingIntent.getStringExtra(KEY_SYSTEM_URL);
+        long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0);
+        long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0);
+
+        sVerifiedUrl = url;
+
+        // start service
+        Intent intent = new Intent(this, DynamicAndroidInstallationService.class);
+        intent.setAction(DynamicAndroidClient.ACTION_START_INSTALL);
+        intent.putExtra(KEY_SYSTEM_URL, url);
+        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+        Log.d(TAG, "Starting Installation Service");
+        startServiceAsUser(intent, UserHandle.SYSTEM);
+    }
+
+    static boolean isVerified(String url) {
+        return sVerifiedUrl != null && sVerifiedUrl.equals(url);
+    }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index c5e598d..31a3ff4 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -221,6 +221,12 @@
     }
 
     @Override
+    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+        // we use the version with channel, so this is never called.
+        return null;
+    }
+
+    @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
             NotificationChannel channel) {
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index f372fe5..24fa87a 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -15,11 +15,10 @@
  */
 package android.ext.services.notification;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Person;
 import android.app.RemoteAction;
+import android.app.RemoteInput;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -27,7 +26,9 @@
 import android.os.Process;
 import android.service.notification.NotificationAssistantService;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.LruCache;
+import android.util.Pair;
 import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassificationContext;
@@ -35,6 +36,8 @@
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextClassifierEvent;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
@@ -43,11 +46,13 @@
 import java.util.Collections;
 import java.util.Deque;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class SmartActionsHelper {
     private static final String KEY_ACTION_TYPE = "action_type";
+    private static final String KEY_ACTION_SCORE = "action_score";
     // If a notification has any of these flags set, it's inelgibile for actions being added.
     private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
             Notification.FLAG_ONGOING_EVENT
@@ -58,75 +63,136 @@
 
     private static final List<String> HINTS =
             Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
+    private static final ConversationActions EMPTY_CONVERSATION_ACTIONS =
+            new ConversationActions(Collections.emptyList(), null);
 
     private Context mContext;
-    @Nullable
     private TextClassifier mTextClassifier;
-    @NonNull
     private AssistantSettings mSettings;
-    private LruCache<String, String> mNotificationKeyToResultIdCache =
-            new LruCache<>(MAX_RESULT_ID_TO_CACHE);
+    private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE);
 
     SmartActionsHelper(Context context, AssistantSettings settings) {
         mContext = context;
         TextClassificationManager textClassificationManager =
                 mContext.getSystemService(TextClassificationManager.class);
-        if (textClassificationManager != null) {
-            mTextClassifier = textClassificationManager.getTextClassifier();
-        }
+        mTextClassifier = textClassificationManager.getTextClassifier();
         mSettings = settings;
     }
 
-    SmartSuggestions suggest(@NonNull NotificationEntry entry) {
+    SmartSuggestions suggest(NotificationEntry entry) {
         // Whenever suggest() is called on a notification, its previous session is ended.
-        mNotificationKeyToResultIdCache.remove(entry.getSbn().getKey());
+        mSessionCache.remove(entry.getSbn().getKey());
 
         boolean eligibleForReplyAdjustment =
                 mSettings.mGenerateReplies && isEligibleForReplyAdjustment(entry);
         boolean eligibleForActionAdjustment =
                 mSettings.mGenerateActions && isEligibleForActionAdjustment(entry);
 
-        List<ConversationAction> conversationActions =
+        ConversationActions conversationActionsResult =
                 suggestConversationActions(
                         entry,
                         eligibleForReplyAdjustment,
                         eligibleForActionAdjustment);
 
-        ArrayList<CharSequence> replies = conversationActions.stream()
-                .map(ConversationAction::getTextReply)
-                .filter(textReply -> !TextUtils.isEmpty(textReply))
-                .collect(Collectors.toCollection(ArrayList::new));
+        String resultId = conversationActionsResult.getId();
+        List<ConversationAction> conversationActions =
+                conversationActionsResult.getConversationActions();
+
+        ArrayList<CharSequence> replies = new ArrayList<>();
+        Map<CharSequence, Float> repliesScore = new ArrayMap<>();
+        for (ConversationAction conversationAction : conversationActions) {
+            CharSequence textReply = conversationAction.getTextReply();
+            if (TextUtils.isEmpty(textReply)) {
+                continue;
+            }
+            replies.add(textReply);
+            repliesScore.put(textReply, conversationAction.getConfidenceScore());
+        }
 
         ArrayList<Notification.Action> actions = conversationActions.stream()
                 .filter(conversationAction -> conversationAction.getAction() != null)
-                .map(action -> createNotificationAction(action.getAction(), action.getType()))
+                .map(action -> createNotificationAction(
+                        action.getAction(), action.getType(), action.getConfidenceScore()))
                 .collect(Collectors.toCollection(ArrayList::new));
+
+        // Start a new session for logging if necessary.
+        if (!TextUtils.isEmpty(resultId)
+                && !conversationActions.isEmpty()
+                && suggestionsMightBeUsedInNotification(
+                entry, !actions.isEmpty(), !replies.isEmpty())) {
+            mSessionCache.put(entry.getSbn().getKey(), new Session(resultId, repliesScore));
+        }
+
         return new SmartSuggestions(replies, actions);
     }
 
     /**
+     * Returns whether the suggestion might be used in the notifications in SysUI.
+     * <p>
+     * Currently, NAS has no idea if suggestions will actually be used in the notification, and thus
+     * this function tries to make a heuristic. This function tries to optimize the precision,
+     * that means when it is unsure, it will return false. The objective is to avoid false positive,
+     * which could pollute the log and CTR as we are logging click rate of suggestions that could
+     * be never visible to users. On the other hand, it is fine to have false negative because
+     * it would be just like sampling.
+     */
+    private boolean suggestionsMightBeUsedInNotification(
+            NotificationEntry notificationEntry, boolean hasSmartAction, boolean hasSmartReply) {
+        Notification notification = notificationEntry.getNotification();
+        boolean hasAppGeneratedContextualActions = !notification.getContextualActions().isEmpty();
+
+        Pair<RemoteInput, Notification.Action> freeformRemoteInputAndAction =
+                notification.findRemoteInputActionPair(/* requiresFreeform */ true);
+        boolean hasAppGeneratedReplies = false;
+        boolean allowGeneratedReplies = false;
+        if (freeformRemoteInputAndAction != null) {
+            RemoteInput freeformRemoteInput = freeformRemoteInputAndAction.first;
+            Notification.Action actionWithFreeformRemoteInput = freeformRemoteInputAndAction.second;
+            hasAppGeneratedReplies = !ArrayUtils.isEmpty(freeformRemoteInput.getChoices());
+            allowGeneratedReplies = actionWithFreeformRemoteInput.getAllowGeneratedReplies();
+        }
+
+        if (hasAppGeneratedReplies || hasAppGeneratedContextualActions) {
+            return false;
+        }
+        return hasSmartAction && notification.getAllowSystemGeneratedContextualActions()
+                || hasSmartReply && allowGeneratedReplies;
+    }
+
+    private void reportActionsGenerated(
+            String resultId, List<ConversationAction> conversationActions) {
+        if (TextUtils.isEmpty(resultId)) {
+            return;
+        }
+        TextClassifierEvent textClassifierEvent =
+                createTextClassifierEventBuilder(
+                        TextClassifierEvent.TYPE_ACTIONS_GENERATED, resultId)
+                        .setEntityTypes(conversationActions.stream()
+                                .map(ConversationAction::getType)
+                                .toArray(String[]::new))
+                        .build();
+        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+    }
+
+    /**
      * Adds action adjustments based on the notification contents.
      */
-    @NonNull
-    private List<ConversationAction> suggestConversationActions(
-            @NonNull NotificationEntry entry,
+    private ConversationActions suggestConversationActions(
+            NotificationEntry entry,
             boolean includeReplies,
             boolean includeActions) {
         if (!includeReplies && !includeActions) {
-            return Collections.emptyList();
-        }
-        if (mTextClassifier == null) {
-            return Collections.emptyList();
+            return EMPTY_CONVERSATION_ACTIONS;
         }
         List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
         if (messages.isEmpty()) {
-            return Collections.emptyList();
+            return EMPTY_CONVERSATION_ACTIONS;
         }
         // Do not generate smart actions if the last message is from the local user.
         ConversationActions.Message lastMessage = messages.get(messages.size() - 1);
         if (arePersonsEqual(
                 ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) {
-            return Collections.emptyList();
+            return EMPTY_CONVERSATION_ACTIONS;
         }
 
         TextClassifier.EntityConfig.Builder typeConfigBuilder =
@@ -146,25 +212,20 @@
                         .setHints(HINTS)
                         .setTypeConfig(typeConfigBuilder.build())
                         .build();
-
-        ConversationActions conversationActionsResult =
+        ConversationActions conversationActions =
                 mTextClassifier.suggestConversationActions(request);
-
-        String resultId = conversationActionsResult.getId();
-        if (!TextUtils.isEmpty(resultId)
-                && !conversationActionsResult.getConversationActions().isEmpty()) {
-            mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId);
-        }
-        return conversationActionsResult.getConversationActions();
+        reportActionsGenerated(
+                conversationActions.getId(), conversationActions.getConversationActions());
+        return conversationActions;
     }
 
-    void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction,
+    void onNotificationExpansionChanged(NotificationEntry entry, boolean isUserAction,
             boolean isExpanded) {
         if (!isExpanded) {
             return;
         }
-        String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey());
-        if (resultId == null) {
+        Session session = mSessionCache.get(entry.getSbn().getKey());
+        if (session == null) {
             return;
         }
         // Only report if this is the first time the user sees these suggestions.
@@ -173,56 +234,50 @@
         }
         entry.setShowActionEventLogged();
         TextClassifierEvent textClassifierEvent =
-                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN,
-                        resultId)
+                createTextClassifierEventBuilder(
+                        TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId)
                         .build();
         // TODO: If possible, report which replies / actions are actually seen by user.
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
 
-    void onNotificationDirectReplied(@NonNull String key) {
-        if (mTextClassifier == null) {
-            return;
-        }
-        String resultId = mNotificationKeyToResultIdCache.get(key);
-        if (resultId == null) {
+    void onNotificationDirectReplied(String key) {
+        Session session = mSessionCache.get(key);
+        if (session == null) {
             return;
         }
         TextClassifierEvent textClassifierEvent =
-                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId)
+                createTextClassifierEventBuilder(
+                        TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId)
                         .build();
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
 
-    void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+    void onSuggestedReplySent(String key, CharSequence reply,
             @NotificationAssistantService.Source int source) {
-        if (mTextClassifier == null) {
-            return;
-        }
         if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
             return;
         }
-        String resultId = mNotificationKeyToResultIdCache.get(key);
-        if (resultId == null) {
+        Session session = mSessionCache.get(key);
+        if (session == null) {
             return;
         }
         TextClassifierEvent textClassifierEvent =
-                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+                createTextClassifierEventBuilder(
+                        TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
                         .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
+                        .setScore(session.repliesScores.getOrDefault(reply, 0f))
                         .build();
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
 
-    void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+    void onActionClicked(String key, Notification.Action action,
             @NotificationAssistantService.Source int source) {
-        if (mTextClassifier == null) {
-            return;
-        }
         if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
             return;
         }
-        String resultId = mNotificationKeyToResultIdCache.get(key);
-        if (resultId == null) {
+        Session session = mSessionCache.get(key);
+        if (session == null) {
             return;
         }
         String actionType = action.getExtras().getString(KEY_ACTION_TYPE);
@@ -230,28 +285,32 @@
             return;
         }
         TextClassifierEvent textClassifierEvent =
-                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+                createTextClassifierEventBuilder(
+                        TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
                         .setEntityTypes(actionType)
                         .build();
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
 
     private Notification.Action createNotificationAction(
-            RemoteAction remoteAction, String actionType) {
+            RemoteAction remoteAction, String actionType, float score) {
         Icon icon = remoteAction.shouldShowIcon()
                 ? remoteAction.getIcon()
                 : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open);
+        Bundle extras = new Bundle();
+        extras.putString(KEY_ACTION_TYPE, actionType);
+        extras.putFloat(KEY_ACTION_SCORE, score);
         return new Notification.Action.Builder(
                 icon,
                 remoteAction.getTitle(),
                 remoteAction.getActionIntent())
                 .setContextual(true)
-                .addExtras(Bundle.forPair(KEY_ACTION_TYPE, actionType))
+                .addExtras(extras)
                 .build();
     }
 
     private TextClassifierEvent.Builder createTextClassifierEventBuilder(
-            int eventType, @NonNull String resultId) {
+            int eventType, String resultId) {
         return new TextClassifierEvent.Builder(
                 TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType)
                 .setEventTime(System.currentTimeMillis())
@@ -269,7 +328,7 @@
      * to fundamental phone functionality where any error would result in a very negative user
      * experience.
      */
-    private boolean isEligibleForActionAdjustment(@NonNull NotificationEntry entry) {
+    private boolean isEligibleForActionAdjustment(NotificationEntry entry) {
         Notification notification = entry.getNotification();
         String pkg = entry.getSbn().getPackageName();
         if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
@@ -285,7 +344,7 @@
         return entry.isMessaging();
     }
 
-    private boolean isEligibleForReplyAdjustment(@NonNull NotificationEntry entry) {
+    private boolean isEligibleForReplyAdjustment(NotificationEntry entry) {
         if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
             return false;
         }
@@ -306,8 +365,7 @@
     }
 
     /** Returns the text most salient for action extraction in a notification. */
-    @Nullable
-    private List<ConversationActions.Message> extractMessages(@NonNull Notification notification) {
+    private List<ConversationActions.Message> extractMessages(Notification notification) {
         Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
         if (messages == null || messages.length == 0) {
             return Collections.singletonList(new ConversationActions.Message.Builder(
@@ -343,7 +401,7 @@
         return new ArrayList<>(extractMessages);
     }
 
-    private static boolean arePersonsEqual(@NonNull Person left, @NonNull Person right) {
+    private static boolean arePersonsEqual(Person left, Person right) {
         return Objects.equals(left.getKey(), right.getKey())
                 && Objects.equals(left.getName(), right.getName())
                 && Objects.equals(left.getUri(), right.getUri());
@@ -359,4 +417,14 @@
             this.actions = actions;
         }
     }
+
+    private static class Session {
+        public final String resultId;
+        public final Map<CharSequence, Float> repliesScores;
+
+        Session(String resultId, Map<CharSequence, Float> repliesScores) {
+            this.resultId = resultId;
+            this.repliesScores = repliesScores;
+        }
+    }
 }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
index 74c20fc..d0b6d00 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
@@ -19,7 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -53,8 +55,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.time.Instant;
@@ -71,9 +73,12 @@
 public class SmartActionsHelperTest {
     private static final String NOTIFICATION_KEY = "key";
     private static final String RESULT_ID = "id";
+    private static final float SCORE = 0.7f;
+    private static final CharSequence SMART_REPLY = "Home";
     private static final ConversationAction REPLY_ACTION =
             new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
-                    .setTextReply("Home")
+                    .setTextReply(SMART_REPLY)
+                    .setConfidenceScore(SCORE)
                     .build();
     private static final String MESSAGE = "Where are you?";
 
@@ -197,8 +202,16 @@
 
         List<ConversationActions.Message> messages =
                 runSuggestAndCaptureRequest().getConversation();
+
         assertThat(messages).hasSize(1);
         MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE);
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertThat(textClassifierEvent.getEntityTypes()).asList()
+                .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
     }
 
     @Test
@@ -249,6 +262,14 @@
         MessageSubject.assertThat(fourthMessage).hasPerson(userB);
         MessageSubject.assertThat(fourthMessage)
                 .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
+
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertThat(textClassifierEvent.getEntityTypes()).asList()
+                .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
     }
 
     @Test
@@ -299,13 +320,15 @@
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onSuggestedReplySent(
-                NOTIFICATION_KEY, MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+                NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
-        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
-        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
-        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION);
+        verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
+        List<TextClassifierEvent> events = argumentCaptor.getAllValues();
+        assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION);
+        assertThat(events.get(1).getScore()).isEqualTo(SCORE);
     }
 
     @Test
@@ -317,24 +340,22 @@
         mSmartActionsHelper.onSuggestedReplySent(
                 "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
 
-        verify(mTextClassifier, never())
-                .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+        verify(mTextClassifier, never()).onTextClassifierEvent(
+                argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION)));
     }
 
     @Test
     public void testOnSuggestedReplySent_missingResultId() {
         when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
-                .thenReturn(new ConversationActions(Collections.emptyList(), null));
-
+                .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
         Notification notification = createMessageNotification();
         when(mStatusBarNotification.getNotification()).thenReturn(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onSuggestedReplySent(
-                "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+                NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
 
-        verify(mTextClassifier, never())
-                .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+        verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
     }
 
     @Test
@@ -347,9 +368,10 @@
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
-        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
-        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
-        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY);
+        verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
+        List<TextClassifierEvent> events = argumentCaptor.getAllValues();
+        assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY);
     }
 
     @Test
@@ -362,9 +384,10 @@
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
-        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
-        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
-        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+        verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
+        List<TextClassifierEvent> events = argumentCaptor.getAllValues();
+        assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
     }
 
     @Test
@@ -376,7 +399,7 @@
         mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false, false);
 
         verify(mTextClassifier, never()).onTextClassifierEvent(
-                Mockito.any(TextClassifierEvent.class));
+                argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN)));
     }
 
     @Test
@@ -389,9 +412,10 @@
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
-        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
-        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
-        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+        verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
+        List<TextClassifierEvent> events = argumentCaptor.getAllValues();
+        assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
+        assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
     }
 
     private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
@@ -490,4 +514,21 @@
             return assertAbout(FACTORY).that(message);
         }
     }
+
+    private final class TextClassifierEventMatcher implements ArgumentMatcher<TextClassifierEvent> {
+
+        private int mType;
+
+        private TextClassifierEventMatcher(int type) {
+            mType = type;
+        }
+
+        @Override
+        public boolean matches(TextClassifierEvent textClassifierEvent) {
+            if (textClassifierEvent == null) {
+                return false;
+            }
+            return mType == textClassifierEvent.getEventType();
+        }
+    }
 }
diff --git a/packages/FakeOemFeatures/Android.bp b/packages/FakeOemFeatures/Android.bp
new file mode 100644
index 0000000..b265158
--- /dev/null
+++ b/packages/FakeOemFeatures/Android.bp
@@ -0,0 +1,9 @@
+android_app {
+    name: "FakeOemFeatures",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
new file mode 100644
index 0000000..e794f72
--- /dev/null
+++ b/packages/FusedLocation/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_app {
+    name: "FusedLocation",
+    srcs: ["**/*.java"],
+    libs: ["com.android.location.provider"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/LocalTransport/Android.bp b/packages/LocalTransport/Android.bp
new file mode 100644
index 0000000..2c990fe
--- /dev/null
+++ b/packages/LocalTransport/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+    name: "LocalTransport",
+    srcs: ["src/**/*.java"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/LocalTransport/Android.mk b/packages/LocalTransport/Android.mk
deleted file mode 100644
index 3484b0f..0000000
--- a/packages/LocalTransport/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_PACKAGE_NAME := LocalTransport
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index d656593..b700bf3 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -35,11 +35,12 @@
 android_app {
     name: "NetworkStack",
     sdk_version: "system_current",
-    certificate: "platform",
+    certificate: "networkstack",
     privileged: true,
     static_libs: [
         "NetworkStackLib"
     ],
+    jarjar_rules: "jarjar-rules-shared.txt",
     manifest: "AndroidManifest.xml",
     required: ["NetworkStackPermissionStub"],
 }
\ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index e4d3591..52c209e 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,20 +17,19 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.mainline.networkstack"
+          package="com.android.networkstack"
           android:sharedUserId="android.uid.networkstack">
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <!-- Signature permission defined in NetworkStackStub -->
     <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
     <!-- Send latency broadcast as current user -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-    <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <application
         android:label="NetworkStack"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt
new file mode 100644
index 0000000..a8c712a
--- /dev/null
+++ b/packages/NetworkStack/jarjar-rules-shared.txt
@@ -0,0 +1,19 @@
+rule com.android.internal.util.** android.net.networkstack.util.@1
+
+rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1
+rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1
+
+# Ignore DhcpResultsParcelable, but jarjar DhcpResults
+# TODO: move DhcpResults into services.net and delete from here
+rule android.net.DhcpResultsParcelable* @0
+rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
+rule android.net.LocalLog* android.net.networkstack.LocalLog@1
+
+# TODO: remove from framework dependencies, then remove here
+rule android.net.InterfaceConfigurationParcel* android.net.networkstack.InterfaceConfigurationParcel@1
+rule android.net.TetherStatsParcel* android.net.networkstack.TetherStatsParcel@1
+
+# Used by UidRange, which is used by framework classes such as NetworkCapabilities.
+rule android.net.UidRangeParcel* android.net.networkstack.UidRangeParcel@1
+# TODO: move TcpKeepalivePacketData to services.net and delete
+rule android.net.TcpKeepalivePacketDataParcelable* android.net.networkstack.TcpKeepalivePacketDataParcelable@1
\ No newline at end of file
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
index 96d1a28..97d26c7 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.shared.FdEventsReader;
+import android.net.util.FdEventsReader;
 import android.os.Handler;
 import android.system.Os;
 
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 9e59912..b1f6d24 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -46,6 +46,7 @@
 import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.ConditionVariable;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -380,6 +381,13 @@
         public InterfaceParams getInterfaceParams(String ifname) {
             return InterfaceParams.getByName(ifname);
         }
+
+        /**
+         * Get a INetd connector.
+         */
+        public INetd getNetd(Context context) {
+            return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
+        }
     }
 
     public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -413,7 +421,7 @@
 
         // TODO: Consider creating, constructing, and passing in some kind of
         // InterfaceController.Dependencies class.
-        mNetd = mContext.getSystemService(INetd.class);
+        mNetd = deps.getNetd(mContext);
         mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
 
         mLinkObserver = new IpClientLinkObserver(
diff --git a/core/java/android/net/shared/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
similarity index 98%
rename from core/java/android/net/shared/FdEventsReader.java
rename to packages/NetworkStack/src/android/net/util/FdEventsReader.java
index bffbfb1..1380ea7 100644
--- a/core/java/android/net/shared/FdEventsReader.java
+++ b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.net.shared;
+package android.net.util;
 
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.util.SocketUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java
index 94b1e9f..4aec6b6 100644
--- a/packages/NetworkStack/src/android/net/util/PacketReader.java
+++ b/packages/NetworkStack/src/android/net/util/PacketReader.java
@@ -18,7 +18,6 @@
 
 import static java.lang.Math.max;
 
-import android.net.shared.FdEventsReader;
 import android.os.Handler;
 import android.system.Os;
 
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index cedcb84..90db207 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -114,7 +114,8 @@
 
         NetworkStackConnector(Context context) {
             mContext = context;
-            mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE);
+            mNetd = INetd.Stub.asInterface(
+                    (IBinder) context.getSystemService(Context.NETD_SERVICE));
             mObserverRegistry = new NetworkObserverRegistry();
             mCm = context.getSystemService(ConnectivityManager.class);
 
@@ -246,6 +247,12 @@
         }
 
         @Override
+        public void notifyCaptivePortalAppFinished(int response) {
+            checkNetworkStackCallingPermission();
+            mNm.notifyCaptivePortalAppFinished(response);
+        }
+
+        @Override
         public void forceReevaluation(int uid) {
             checkNetworkStackCallingPermission();
             mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 2e72d82..4b846b0 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -39,9 +39,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
-import android.net.ICaptivePortal;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.LinkProperties;
@@ -67,15 +65,9 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.telephony.CellIdentityCdma;
-import android.telephony.CellIdentityGsm;
-import android.telephony.CellIdentityLte;
-import android.telephony.CellIdentityWcdma;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoCdma;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -466,6 +458,13 @@
         sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
     }
 
+    /**
+     * Notify that the captive portal app was closed with the provided response code.
+     */
+    public void notifyCaptivePortalAppFinished(int response) {
+        sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
+    }
+
     @Override
     protected void log(String s) {
         if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
@@ -677,29 +676,8 @@
                 case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
                     final Bundle appExtras = new Bundle();
                     // OneAddressPerFamilyNetwork is not parcelable across processes.
-                    appExtras.putParcelable(
-                            ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
-                    appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
-                            new CaptivePortal(new ICaptivePortal.Stub() {
-                                @Override
-                                public void appResponse(int response) {
-                                    if (response == APP_RETURN_WANTED_AS_IS) {
-                                        mContext.enforceCallingPermission(
-                                                PERMISSION_NETWORK_SETTINGS,
-                                                "CaptivePortal");
-                                    }
-                                    sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
-                                }
-
-                                @Override
-                                public void logEvent(int eventId, String packageName)
-                                        throws RemoteException {
-                                    mContext.enforceCallingPermission(
-                                            PERMISSION_NETWORK_SETTINGS,
-                                            "CaptivePortal");
-                                    mCallback.logCaptivePortalLoginEvent(eventId, packageName);
-                                }
-                            }));
+                    final Network network = new Network(mNetwork);
+                    appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
                     final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
                     appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
                     if (probeRes.probeSpec != null) {
@@ -708,7 +686,7 @@
                     }
                     appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
                             mCaptivePortalUserAgent);
-                    mCm.startCaptivePortalApp(appExtras);
+                    mCm.startCaptivePortalApp(network, appExtras);
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
@@ -1312,6 +1290,7 @@
             urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
             urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
             urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+            urlConnection.setRequestProperty("Connection", "close");
             urlConnection.setUseCaches(false);
             if (mCaptivePortalUserAgent != null) {
                 urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent);
@@ -1339,26 +1318,28 @@
             // is needed (i.e. can't browse a 204).  This could be the result of an HTTP
             // proxy server.
             if (httpResponseCode == 200) {
+                long contentLength = urlConnection.getContentLengthLong();
                 if (probeType == ValidationProbeEvent.PROBE_PAC) {
                     validationLog(
                             probeType, url, "PAC fetch 200 response interpreted as 204 response.");
                     httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
-                } else if (urlConnection.getContentLengthLong() == 0) {
-                    // Consider 200 response with "Content-length=0" to not be a captive portal.
-                    // There's no point in considering this a captive portal as the user cannot
-                    // sign-in to an empty page. Probably the result of a broken transparent proxy.
-                    // See http://b/9972012.
-                    validationLog(probeType, url,
-                            "200 response with Content-length=0 interpreted as 204 response.");
-                    httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
-                } else if (urlConnection.getContentLengthLong() == -1) {
-                    // When no Content-length (default value == -1), attempt to read a byte from the
-                    // response. Do not use available() as it is unreliable. See http://b/33498325.
+                } else if (contentLength == -1) {
+                    // When no Content-length (default value == -1), attempt to read a byte
+                    // from the response. Do not use available() as it is unreliable.
+                    // See http://b/33498325.
                     if (urlConnection.getInputStream().read() == -1) {
-                        validationLog(
-                                probeType, url, "Empty 200 response interpreted as 204 response.");
-                        httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
+                        validationLog(probeType, url,
+                                "Empty 200 response interpreted as failed response.");
+                        httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
                     }
+                } else if (contentLength <= 4) {
+                    // Consider 200 response with "Content-length <= 4" to not be a captive
+                    // portal. There's no point in considering this a captive portal as the
+                    // user cannot sign-in to an empty page. Probably the result of a broken
+                    // transparent proxy. See http://b/9972012 and http://b/122999481.
+                    validationLog(probeType, url, "200 response with Content-length <= 4"
+                            + " interpreted as failed response.");
+                    httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
                 }
             }
         } catch (IOException e) {
@@ -1485,10 +1466,6 @@
      */
     private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
             long requestTimestampMs, long responseTimestampMs) {
-        if (!mWifiManager.isScanAlwaysAvailable()) {
-            return;
-        }
-
         if (!mSystemReady) {
             return;
         }
@@ -1496,6 +1473,10 @@
         Intent latencyBroadcast =
                 new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
         if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+            if (!mWifiManager.isScanAlwaysAvailable()) {
+                return;
+            }
+
             WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
             if (currentWifiInfo != null) {
                 // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
@@ -1515,39 +1496,21 @@
             }
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
         } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            // TODO(b/123893112): Support multi-sim.
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
                     mTelephonyManager.getNetworkType());
-            List<CellInfo> info = mTelephonyManager.getAllCellInfo();
-            if (info == null) return;
-            int numRegisteredCellInfo = 0;
-            for (CellInfo cellInfo : info) {
-                if (cellInfo.isRegistered()) {
-                    numRegisteredCellInfo++;
-                    if (numRegisteredCellInfo > 1) {
-                        if (VDBG) {
-                            logw("more than one registered CellInfo."
-                                    + " Can't tell which is active.  Bailing.");
-                        }
-                        return;
-                    }
-                    if (cellInfo instanceof CellInfoCdma) {
-                        CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoGsm) {
-                        CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoLte) {
-                        CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoWcdma) {
-                        CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else {
-                        if (VDBG) logw("Registered cellinfo is unrecognized");
-                        return;
-                    }
-                }
+            final ServiceState dataSs = mTelephonyManager.getServiceState();
+            if (dataSs == null) {
+                logw("failed to retrieve ServiceState");
+                return;
             }
+            // See if the data sub is registered for PS services on cell.
+            final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState(
+                    NetworkRegistrationState.DOMAIN_PS,
+                    AccessNetworkConstants.TransportType.WWAN);
+            latencyBroadcast.putExtra(
+                    NetworkMonitorUtils.EXTRA_CELL_ID,
+                    nrs == null ? null : nrs.getCellIdentity());
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
         } else {
             return;
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 4a09b3e..5c7b514 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -18,6 +18,7 @@
     name: "NetworkStackTests",
     certificate: "platform",
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     resource_dirs: ["res"],
     static_libs: [
         "android-support-test",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index a4a1000..3414397 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -22,6 +22,7 @@
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_STREAM;
 
@@ -39,9 +40,7 @@
 import android.content.Context;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.SocketKeepalive;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+import android.net.TcpKeepalivePacketDataParcelable;
 import android.net.apf.ApfFilter.ApfConfiguration;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
@@ -1017,6 +1016,7 @@
     private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
     private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
     private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
+    private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
     private static final byte[] IPV4_BROADCAST_ADDRESS =
             {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
 
@@ -1544,12 +1544,15 @@
         InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
         InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
 
-        final TcpSocketInfo v4Tsi = new TcpSocketInfo(
-                srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
-        final TcpKeepalivePacketData ipv4TcpKeepalivePacket =
-                TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi);
+        final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+        parcel.srcAddress = srcAddr.getAddress();
+        parcel.srcPort = srcPort;
+        parcel.dstAddress = dstAddr.getAddress();
+        parcel.dstPort = dstPort;
+        parcel.seq = seqNum;
+        parcel.ack = ackNum;
 
-        apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+        apfFilter.addKeepalivePacketFilter(slot1, parcel);
         program = cb.getApfProgram();
 
         // Verify IPv4 keepalive ack packet is dropped
@@ -1568,7 +1571,7 @@
         // Verify IPv4 packet from another address is passed
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                        anotherDstPort, anotherSeqNum, anotherAckNum));
+                        anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
         // Remove IPv4 keepalive filter
         apfFilter.removeKeepalivePacketFilter(slot1);
@@ -1578,11 +1581,17 @@
             // dst: 2404:0:0:0:0:0:faf2, port: 54321
             srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
             dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
-            final TcpSocketInfo v6Tsi = new TcpSocketInfo(
-                    srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
-            final TcpKeepalivePacketData ipv6TcpKeepalivePacket =
-                    TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi);
-            apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable());
+
+            final TcpKeepalivePacketDataParcelable ipv6Parcel =
+                    new TcpKeepalivePacketDataParcelable();
+            ipv6Parcel.srcAddress = srcAddr.getAddress();
+            ipv6Parcel.srcPort = srcPort;
+            ipv6Parcel.dstAddress = dstAddr.getAddress();
+            ipv6Parcel.dstPort = dstPort;
+            ipv6Parcel.seq = seqNum;
+            ipv6Parcel.ack = ackNum;
+
+            apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel);
             program = cb.getApfProgram();
 
             // Verify IPv6 keepalive ack packet is dropped
@@ -1604,8 +1613,8 @@
             apfFilter.removeKeepalivePacketFilter(slot1);
 
             // Verify multiple filters
-            apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
-            apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable());
+            apfFilter.addKeepalivePacketFilter(slot1, parcel);
+            apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel);
             program = cb.getApfProgram();
 
             // Verify IPv4 keepalive ack packet is dropped
@@ -1613,15 +1622,15 @@
             // dst: 10.0.0.5, port: 12345
             assertDrop(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum, seqNum + 1));
+                            dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
             // Verify IPv4 non-keepalive ack packet from the same source address is passed
             assertPass(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum + 100, seqNum));
+                            dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
             // Verify IPv4 packet from another address is passed
             assertPass(program,
                     ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                            anotherDstPort, anotherSeqNum, anotherAckNum));
+                            anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
             // Verify IPv6 keepalive ack packet is dropped
             // src: 2404:0:0:0:0:0:faf2, port: 54321
@@ -1641,7 +1650,7 @@
             // Remove keepalive filters
             apfFilter.removeKeepalivePacketFilter(slot1);
             apfFilter.removeKeepalivePacketFilter(slot2);
-        } catch (SocketKeepalive.InvalidPacketException e) {
+        } catch (UnsupportedOperationException e) {
             // TODO: support V6 packets
         }
 
@@ -1650,13 +1659,13 @@
         // Verify IPv4, IPv6 packets are passed
         assertPass(program,
                 ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                        dstPort, srcPort, ackNum, seqNum + 1));
+                        dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
                         dstPort, srcPort, ackNum, seqNum + 1));
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
-                        dstPort, anotherSeqNum, anotherAckNum));
+                        dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
                         dstPort, anotherSeqNum, anotherAckNum));
@@ -1664,28 +1673,30 @@
         apfFilter.shutdown();
     }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack) {
-        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+    private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
+            int dport, int seq, int ack, int dataLength) {
+        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+        ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+        // ether type
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+        // IPv4 header
         packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+        packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
         put(packet, IPV4_SRC_ADDR_OFFSET, sip);
-        put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+        put(packet, IPV4_DEST_ADDR_OFFSET, dip);
         packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
         packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
         packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
         packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
-        return packet.array();
-    }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack, int dataLength) {
-        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
-
-        ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
-        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
-        // TCP header length 5, reserved 3 bits, NS=0
+        // TCP header length 5(20 bytes), reserved 3 bits, NS=0
         packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+        // TCP flags: ACK set
+        packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
         return packet.array();
     }
 
diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
index 7e57d1e..aaaff02 100644
--- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
+++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
@@ -104,8 +104,8 @@
 
         when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
         when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
-        when(mContext.getSystemService(INetd.class)).thenReturn(mNetd);
         when(mContext.getResources()).thenReturn(mResources);
+        when(mDependencies.getNetd(any())).thenReturn(mNetd);
         when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                 .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
 
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index b98b0f7..9a16bb7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,8 +16,7 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,8 +40,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitorCallbacks;
 import android.net.InetAddresses;
@@ -54,10 +51,10 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -487,19 +484,23 @@
         // Check that startCaptivePortalApp sends the expected intent.
         nm.launchCaptivePortalApp();
 
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
-                .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
-        final Intent intent = intentCaptor.getValue();
-        assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
-        final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
-        assertEquals(TEST_NETID, network.netId);
+        final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+        verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+        final Bundle bundle = bundleCaptor.getValue();
+        final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+        assertEquals(TEST_NETID, bundleNetwork.netId);
+        // network is passed both in bundle and as parameter, as the bundle is opaque to the
+        // framework and only intended for the captive portal app, but the framework needs
+        // the network to identify the right NetworkMonitor.
+        assertEquals(TEST_NETID, networkCaptor.getValue().netId);
 
         // Have the app report that the captive portal is dismissed, and check that we revalidate.
         setStatus(mHttpsConnection, 204);
         setStatus(mHttpConnection, 204);
-        final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
-        captivePortal.reportCaptivePortalDismissed();
+
+        nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
         verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
                 .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
     }
diff --git a/packages/NetworkStackPermissionStub/Android.bp b/packages/NetworkStackPermissionStub/Android.bp
index 94870c9..dd70cf5 100644
--- a/packages/NetworkStackPermissionStub/Android.bp
+++ b/packages/NetworkStackPermissionStub/Android.bp
@@ -21,7 +21,7 @@
     // a classes.dex.
     srcs: ["src/**/*.java"],
     platform_apis: true,
-    certificate: "platform",
+    certificate: "networkstack",
     privileged: true,
     manifest: "AndroidManifest.xml",
 }
diff --git a/packages/NetworkStackPermissionStub/AndroidManifest.xml b/packages/NetworkStackPermissionStub/AndroidManifest.xml
index 2ccf5ff..a8742d7 100644
--- a/packages/NetworkStackPermissionStub/AndroidManifest.xml
+++ b/packages/NetworkStackPermissionStub/AndroidManifest.xml
@@ -17,7 +17,8 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.mainline.networkstack.permissionstub">
+    package="com.android.networkstack.permissionstub"
+    android:sharedUserId="android.uid.networkstack">
     <!--
     This package only exists to define the below permissions, and enforce that they are only
     granted to apps sharing the same signature.
diff --git a/packages/OsuLogin/Android.bp b/packages/OsuLogin/Android.bp
new file mode 100644
index 0000000..ac3abac
--- /dev/null
+++ b/packages/OsuLogin/Android.bp
@@ -0,0 +1,8 @@
+android_app {
+    name: "OsuLogin",
+    static_libs: ["androidx.legacy_legacy-support-v4"],
+    resource_dirs: ["res"],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/packages/OsuLogin/Android.mk b/packages/OsuLogin/Android.mk
deleted file mode 100644
index 2c07615..0000000
--- a/packages/OsuLogin/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := OsuLogin
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 36ee813..caa928f 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibActionButtonsPreference",
         "SettingsLibEntityHeaderWidgets",
         "SettingsLibBarChartPreference",
+        "SettingsLibProgressBar",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 6d35550..b198f5a 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -52,7 +52,7 @@
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:singleLine="true"
-                  android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                  android:textAppearance="?android:attr/textAppearanceListItem"
                   android:ellipsize="marquee"
                   android:fadingEdge="horizontal"/>
 
@@ -65,7 +65,7 @@
                       android:layout_width="0dp"
                       android:layout_height="wrap_content"
                       android:layout_weight="1"
-                      android:textAppearance="@android:style/TextAppearance.Material.Small"
+                      android:textAppearance="?android:attr/textAppearanceSmall"
                       android:textAlignment="viewStart"
                       android:textColor="?android:attr/textColorSecondary"/>
 
@@ -73,7 +73,7 @@
                       android:layout_width="0dp"
                       android:layout_height="wrap_content"
                       android:layout_weight="1"
-                      android:textAppearance="@android:style/TextAppearance.Material.Small"
+                      android:textAppearance="?android:attr/textAppearanceSmall"
                       android:textAlignment="viewEnd"
                       android:textColor="?android:attr/textColorSecondary"
                       android:maxLines="1"
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index d332bac..3b87fca 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -87,6 +87,7 @@
     };
 
     private int mMaxBarHeight;
+    private boolean mIsLoading;
     private BarChartInfo mBarChartInfo;
 
     public BarChartPreference(Context context) {
@@ -134,12 +135,33 @@
         notifyChanged();
     }
 
+    /**
+     * Set loading state for {@link BarChartPreference}.
+     *
+     * By default, {@link BarChartPreference} doesn't care about it.
+     *
+     * But if user sets loading state to true explicitly, it means {@link BarChartPreference}
+     * needs to take some time to load data. So we won't initialize any view now.
+     *
+     * Once the state is updated to false, we will start to initialize view again.
+     *
+     * @param isLoading whether or not {@link BarChartPreference} is in loading state.
+     */
+    public void updateLoadingState(boolean isLoading) {
+        mIsLoading = isLoading;
+        notifyChanged();
+    }
+
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
         holder.setDividerAllowedAbove(true);
         holder.setDividerAllowedBelow(true);
 
+        // If the state is loading, we just show a blank view.
+        if (mIsLoading) {
+            return;
+        }
         // We must show title of bar chart.
         bindChartTitleView(holder);
 
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
index 9604512..013d2d0 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -23,7 +23,7 @@
     android:layout_marginEnd="16dp"
     android:gravity="center"
     android:clickable="true"
-    android:background="?android:attr/selectableItemBackground"
+    android:background="@*android:drawable/btn_borderless_material"
     android:orientation="vertical">
 
     <ImageView
diff --git a/packages/SettingsLib/ProgressBar/Android.bp b/packages/SettingsLib/ProgressBar/Android.bp
new file mode 100644
index 0000000..eae21d8
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/Android.bp
@@ -0,0 +1,9 @@
+android_library {
+    name: "SettingsLibProgressBar",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/AndroidManifest.xml b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
new file mode 100644
index 0000000..256b8f3
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 0000000..2b7535a
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+    <target
+        android:name="rect2_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" />
+    <target
+        android:name="rect1_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 0000000..2f604d0
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="10dp"
+        android:width="360dp"
+        android:viewportHeight="10"
+        android:viewportWidth="360" >
+    <group
+        android:name="progress_group"
+        android:translateX="180"
+        android:translateY="5" >
+        <path
+            android:name="background_track"
+            android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+            android:fillColor="?android:attr/colorControlActivated"
+            android:fillAlpha="?android:attr/disabledAlpha"/>
+        <group
+            android:name="rect2_grp"
+            android:translateX="-197.60001"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect2"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+        <group
+            android:name="rect1_grp"
+            android:translateX="-522.59998"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect1"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
new file mode 100644
index 0000000..268858b
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="3dp">
+    <View
+        android:id="@+id/progress_bar_background"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorSecondary" />
+    <ProgressBar
+        android:id="@+id/progress_bar_animation"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:indeterminate="true" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/values/styles.xml b/packages/SettingsLib/ProgressBar/res/values/styles.xml
new file mode 100644
index 0000000..5f57c1d
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="TrimmedHorizontalProgressBar"
+           parent="android:Widget.Material.ProgressBar.Horizontal">
+        <item name="android:indeterminateDrawable">
+            @drawable/progress_indeterminate_horizontal_material_trimmed
+        </item>
+        <item name="android:minHeight">3dp</item>
+        <item name="android:maxHeight">3dp</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
new file mode 100644
index 0000000..317e43b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0988cab..cfc76b7 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-toegang"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-oudio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD oudio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Gehoortoestel"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Gekoppel aan gehoortoestel"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Gehoortoestelle"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Gekoppel aan gehoortoestelle"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Gekoppel aan media-oudio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Gekoppel aan foonoudio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Gekoppel aan lêeroordragbediener"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruik vir foonoudio"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruik vir lêeroordrag"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruik vir invoer"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gebruik vir gehoortoestel"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gebruik vir gehoortoestelle"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bind saam"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BIND SAAM"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselleer"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android-bedryfstelsel"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwyderde programme"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwyderde programme en gebruikers"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Stelselopdaterings"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-verbinding"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Wi-Fi-warmkol"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-verbinding"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 301d609..6e6d58f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"የሲም መዳረሻ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"ኤችዲ ኦዲዮ፦ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"ኤችዲ ኦዲዮ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"መስሚያ አጋዥ መሣሪያ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ከመስሚያ አጋዥ መሣሪያ ጋር ተገናኝቷል"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"አጋዥ መስሚያዎች"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ከአጋዥ መስሚያዎች ጋር ተገናኝቷል"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ወደ ማህደረ  መረጃ  አውዲዮ ተያይዟል"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ወደ ስልክ አውዲዮ ተያይዟል"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ወደ ፋይል ዝውውር አገልጋይ ተያይዟል"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ለስልክ ድምፅ ተጠቀም"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ለፋይል ዝውውር ተጠቀም"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ለውፅአት ተጠቀም"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ለመስሚያ አጋዥ መሣሪያ ተጠቀም"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ለአጋዥ መስሚያዎች ይጠቀሙ"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"አጣምር"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"አጣምር"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ይቅር"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android  ስርዓተ ክወና"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"የተወገዱ መተግበሪያዎች"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"የተወገዱ መተግበሪያዎች እና ተጠቃሚዎች"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"የሥርዓት ዝማኔዎች"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB መሰካት"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ተጓጓዥ ድረስ ነጥቦች"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ብሉቱዝ ማያያዝ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 91de9e3..18d47ac 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"‏الوصول إلى شريحة SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"صوت عالي الدقة: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"صوت عالي الدقة"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سماعة الأذن الطبية"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"تم توصيل سماعة الأذن الطبية"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سماعات الأذن الطبية"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"تمّ التوصيل بسماعات الأذن الطبية"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"متصل بالإعدادات الصوتية للوسائط"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"متصل بالإعدادات الصوتية للهاتف"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"متصل بخادم نقل الملف"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"الاستخدام لإعدادات الهاتف الصوتية"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استخدامه لنقل الملفات"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استخدام للإدخال"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"استخدام سماعة الأذن الطبية"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"استخدام سماعات الأذن الطبية"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"إقران"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"إقران"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"إلغاء"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"‏نظام التشغيل Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"التطبيقات المزالة"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"التطبيقات والمستخدمون الذين تمت إزالتهم"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"تحديثات النظام"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏ربط USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطة اتصال محمولة"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ربط البلوتوث"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e726ae4..f865563 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"ছাইন আপ কৰিবলৈ টিপক"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ছাইন ইন কৰা দৰকাৰী"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"একচেছ পইণ্ট কিছু সময়ৰ বাবে পূৰ্ণ হৈ আছে"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$sৰ যোগেৰে সংযোজিত"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খুলি থকা হৈছে"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"সংযোগ কৰিব পৰা নগ’ল"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"ছাইন আপ সম্পূৰ্ণ কৰি থকা হৈছে…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"ছাইন আপ সম্পূৰ্ণ কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰিবলৈ টিপক।"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"ছাইন আপ সম্পূৰ্ণ হৈছে সংযোগ কৰি থকা হৈছে…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"অতি লেহেম"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"লেহেমীয়া"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ঠিক"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ছিম প্ৰৱেশ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"এইচ্ছডি অডিঅ’"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"শ্ৰৱণ যন্ত্ৰ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্ৰৱণ যন্ত্ৰৰ লগত সংযোগ কৰা হ’ল"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"শ্ৰৱণ যন্ত্ৰ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"শ্ৰৱণ যন্ত্ৰলৈ সংযোগ কৰা হৈছে"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিঅ’লৈ সংযোগ হৈছে"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফ’ন অডিঅ\'ৰ লগত সংযোগ কৰা হ’ল"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল ট্ৰান্সফাৰ ছাৰ্ভাৰৰ সৈতে সংযোজিত হৈ আছে"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ফ\'ন অডিঅ\'ৰ বাবে ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ফাইল স্থানান্তৰ কৰিবলৈ ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ইনপুটৰ বাবে ব্যৱহাৰ কৰক"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"যোৰা লগাওক"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"যোৰা লগাওক"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"বাতিল কৰক"</string>
@@ -143,8 +136,10 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"আঁতৰোৱা এপ্‌সমূহ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"আঁতৰোৱা এপ্‌ আৰু ব্যৱহাৰকাৰীসমূহ"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"ইউএছবি টেডাৰিং"</string>
-    <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হ\'টস্প\'ট"</string>
+    <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হটস্পট"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টেডাৰিং"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"টেডাৰ কৰি থকা হৈছে"</string>
     <string name="tether_settings_title_all" msgid="8356136101061143841">"টেডাৰিং আৰু প\'ৰ্টেবল হটস্পট"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 4dd4817..8e6e07d 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Girişi"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Qulaqlıq"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Qulaqlığa qoşuldunuz"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Eşitmə Aparatı"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Eşitmə Aparatlarına qoşuldu"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Media audioya birləşdirilib"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon audiosuna qoşulu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl transfer serverinə qoşulu"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon audiosu istifadə edin"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl transferi üçün istifadə edin"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Daxiletmə üçün istifadə edin"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Qulaqlıq üçün istifadə edin"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Eşitmə Aparatları üçün istifadə edin"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Birləşdir"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CÜTLƏNDİR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ləğv et"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Silinmiş tətbiqlər"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Tətbiqləri və istifadəçiləri silin"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Sistem güncəllənməsi"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Birləşmə"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portativ hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth birləşmə"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index dc54301..1cdd1fd 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM kartici"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano sa slušnim aparatom"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparati"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezano sa slušnim aparatima"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa zvukom telefona"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prenos datoteka"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Korišćenje za audio telefona"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Korišćenje za prenos datoteka"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Koristi za ulaz"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Koristi za slušni aparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Koristi za slušne aparate"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Internet povezivanje"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosni hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth privezivanje"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 2fa2692..1a60c3d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ да SIM-карты"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Аўдыя ў HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Аўдыя ў HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слыхавы апарат"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Падключана да слыхавога апарата"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слыхавыя апараты"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Падключана да слыхавых апаратаў"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Падключана да аўдыё медыа"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Падключана да аўдыё тэлефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Падключаны да серверу перадачы файлаў"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Выкарыстоўваць для аўдыё тэлефона"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Выкарыстоўваць для перадачы файлаў"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Выкарыстоўваць для ўводу"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Выкарыстоўваць для слыхавога апарата"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Выкарыстоўваць для слыхавых апаратаў"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Падлучыць"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СПАЛУЧЫЦЬ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Скасаваць"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"АС Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Выдаленыя прыкладанні"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Выдаленыя прыкладанні і карыстальнiкi"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Абнаўленні сістэмы"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-мадэм"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Партатыўная кропка доступу"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-мадэм"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 1454118..aaedce4 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Достъп до SIM картата"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Висококачествено аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Висококачествено аудио"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слухов апарат"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Има връзка със слуховия апарат"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слухови апарати"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Установена е връзка със слухов апарат"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Установена е връзка с медийно аудио"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Връзка със звука на телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установена е връзка със сървър за трансфер на файлове"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Използване на телефон за аудио"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Използване на за пренос на файлове"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Да се използва за въвеждане"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Използване за слухов апарат"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Използване за слухови апарати"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Сдвояване"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СДВОЯВАНЕ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отказ"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android (ОС)"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Премахнати приложения"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Премахнати приложения и потребители"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Системни актуализации"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Тетъринг през USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносима точка за достъп"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Тетъринг през Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index dbf397f..ef8dd3d 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"সাইন-আপ করতে ট্যাপ করুন"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"কানেক্ট, ইন্টারনেট নেই"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইন্টারনেট কানেকশন নেই"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"সাইন-ইন করা দরকার"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"এই মুহূর্তে অ্যাক্সেস পয়েন্টের কোনও কানেকশন ফাঁকা নেই"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s এর মাধ্যমে পাওয়া যাচ্ছে"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খোলা হচ্ছে"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"কানেক্ট করা যায়নি"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"সাইন-আপ সম্পূর্ণ করা হচ্ছে…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"সাইন-আপ করা যায়নি। আবার চেষ্টা করতে ট্যাপ করুন।"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"সাইন-আপ করা হয়ে গেছে। কানেক্ট করা হচ্ছে…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"খুব ধীরে"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ধীরে"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ঠিক আছে"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"সিম -এর অ্যাক্সেস"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD অডিও: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD অডিও"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"হিয়ারিং এড"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্রবণ যন্ত্রের সাথে কানেক্ট রয়েছে"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"হিয়ারিং এড"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"হিয়ারিং এডের সাথে কানেক্ট করা হয়েছে"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিওতে কানেক্ট রয়েছে"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফোন অডিওতে কানেক্ট"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল স্থানান্তর সার্ভারের সঙ্গে কানেক্ট"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ফোন অডিওয়ের জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ফাইল স্থানান্তরের জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ইনপুটের জন্য ব্যবহার করুন"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"শ্রবণ যন্ত্রের জন্য ব্যবহার করুন"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"হিয়ারিং এডের জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"যুক্ত করুন"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"যুক্ত করুন"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"বাতিল করুন"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB টিথারিং"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"পোর্টেবল হটস্পট"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টিথারিং"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 272e797..4193d52 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano na slušni aparat"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezan na slušne aparate"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano na zvuk telefona"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prijenos podataka"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Koristi za zvuk telefona"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Koristi za prijenos fajlova"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Koristi kao ulaz"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Koristi za slušni aparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Korištenje za slušne aparate"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5d2dd65..9c3e4d9 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accés a la SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Àudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Àudio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiòfon"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"S\'ha connectat a l\'audiòfon"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audiòfons"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"S\'ha connectat als audiòfons"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connectat a l\'àudio del mitjà"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connectat a àudio del telèfon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connectat al servidor de transferència de fitxers"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilitza-ho per a l\'àudio del telèfon"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilitza per a la transferència de fitxers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilitza per a entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilitza per a l\'audiòfon"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilitza per als audiòfons"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincula"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel·la"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacions eliminades"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacions i usuaris eliminats"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Actualitzacions del sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartició de xarxa per USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Punt d\'accés Wi-Fi"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartició de xarxa per Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 19a042e..b6507bc 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Přístup k SIM kartě"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Naslouchátko"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Připojeno k naslouchátku"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Naslouchátka"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Připojeno k naslouchátkům"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Připojeno ke zvukovému médiu"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Připojeno k náhlavní soupravě"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Připojeno k serveru pro přenos dat"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Umožňuje připojení náhlavní soupravy"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použít pro přenos souborů"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použít pro vstup"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Použít pro naslouchátko"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použít pro naslouchátka"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovat"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAT"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušit"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odebrané aplikace"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odebrané aplikace a odebraní uživatelé"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizace systému"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Připojení přes USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Přenosný hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Připojení přes Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index bde67fd..a870cf8 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-adgang"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-lyd"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Høreapparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Der er oprettet forbindelse til høreapparat"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Høreapparater"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Forbundet til høreapparater"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Forbundet til medielyd"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Forbundet til telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Forbundet til filoverførselsserver"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Brug til telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Brug til filoverførsel"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Brug til input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Anvend til høreapparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Brug til høreapparater"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Par"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCEPTÉR PARRING"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuller"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apps og brugere"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Systemopdateringer"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Netdeling via USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Netdeling via Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 543e8f8..c1d502e 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Zugriff auf SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-Audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hörhilfe"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Mit Hörhilfe verbunden"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hörhilfen"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Mit Hörhilfen verbunden"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbunden mit Medien-Audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbunden mit Telefon-Audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Mit Dateiübertragungsserver verbunden"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Für Telefon-Audio verwenden"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Für Dateiübertragung verwenden"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Für Eingabe verwenden"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Für Hörhilfe verwenden"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Für Hörhilfen verwenden"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppeln"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELN"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Abbrechen"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Entfernte Apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Entfernte Apps und Nutzer"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Systemupdates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-Tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobiler Hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-Tethering"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index f7ec12b..22c1b26 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Πρόσβαση SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Ήχος HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Ήχος HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Βοήθημα ακοής"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Συνδέθηκε σε βοήθημα ακοής"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Βοηθήματα ακοής"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Έγινε σύνδεση σε βοηθήματα ακοής"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Συνδέθηκε σε ήχο πολυμέσων"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Συνδεδεμένο στον ήχο τηλεφώνου"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Συνδεδεμένο σε διακομιστή μεταφοράς αρχείων"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Χρήση για ήχο τηλεφώνου"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Χρήση για τη μεταφορά αρχείων"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Χρήση για είσοδο"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Χρήση για βοήθημα ακοής"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Χρήση για βοηθήματα ακοής"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Σύζευξη"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ΣΥΖΕΥΞΗ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ακύρωση"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Λειτουργικό σύστημα Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Εφαρμογές που καταργήθηκαν"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Εφαρμογές και χρήστες που έχουν καταργηθεί"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ενημερώσεις συστήματος"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Πρόσδεση USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Φορητό σημείο πρόσβασης"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Πρόσδεση Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b9d3093..d4f4e5e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index b9d3093..d4f4e5e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b9d3093..d4f4e5e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b9d3093..d4f4e5e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 2d3e830..98b63ee 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎SIM Access‎‏‎‎‏‎"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎HD audio: ‎‏‎‎‏‏‎<xliff:g id="CODEC_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎HD audio‎‏‎‎‏‎"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎Hearing Aid‎‏‎‎‏‎"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎Connected to Hearing Aid‎‏‎‎‏‎"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎Hearing Aids‎‏‎‎‏‎"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎Connected to Hearing Aids‎‏‎‎‏‎"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎Connected to media audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎Connected to phone audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎Connected to file transfer server‎‏‎‎‏‎"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎Use for phone audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‎Use for file transfer‎‏‎‎‏‎"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎Use for input‎‏‎‎‏‎"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎Use for Hearing Aid‎‏‎‎‏‎"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎Use for Hearing Aids‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎Pair‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎PAIR‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎Cancel‎‏‎‎‏‎"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎Android OS‎‏‎‎‏‎"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎Removed apps‎‏‎‎‏‎"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎Removed apps and users‎‏‎‎‏‎"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎System updates‎‏‎‎‏‎"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎USB tethering‎‏‎‎‏‎"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎Portable hotspot‎‏‎‎‏‎"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎Bluetooth tethering‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 00f85e6..e803abd 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio en HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audífonos"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a un audífono"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audífonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audífonos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio multimedia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del dispositivo"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado al servidor de transferencia de archivo"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para el audio del dispositivo"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para la transferencia de archivos"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audífonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para audífonos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión mediante USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión mediante Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b21b7d1..293eff1 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso a tarjeta SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audífonos"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a audífono"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audífonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audífonos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio del medio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del teléfono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado con el servidor de transferencia de archivos"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para audio del teléfono"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uso de la transferencia de archivos"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audífonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar con audífonos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usuarios y aplicaciones eliminados"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartir conexión por USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartir conexión por Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fab1375..14ac827 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kaardi juurdepääs"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-heli: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-heli"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Kuuldeaparaat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Kuuldeaparaadiga ühendatud"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Kuuldeaparaadid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Kuuldeaparaatidega ühendatud"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ühendatud meediumiheliga"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ühendatud telefoniheliga"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ühendatud failiedastuse serveriga"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Kasuta telefoniheli jaoks"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Kasutage failide edastamiseks"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kasutage sisendi jaoks"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Kuuldeaparaadiga kasutamiseks"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Kasuta kuulmisaparaatide puhul"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seo"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEO"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Tühista"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eemaldatud rakendused"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eemaldatud rakendused ja kasutajad"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Süsteemivärskendused"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB jagamine"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobiilne kuumkoht"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jagamine Bluetoothiga"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 7d3e1f3..04710c7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM txartelerako sarbidea"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Kalitate handiko audioa: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Kalitate handiko audioa"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiofonoa"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Audiofonora konektatuta"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audifonoak"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Audifonoetara konektatuta"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Euskarriaren audiora konektatuta"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefonoaren audiora konektatuta"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fitxategi-transferentziako zerbitzarira konektatuta"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Erabili telefonoaren audiorako"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Erabili fitxategi-transferentziarako"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Erabili idazketarako"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Erabili audiofonorako"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Erabili audifonoak"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parekatu"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREKATU"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Utzi"</string>
@@ -121,8 +121,8 @@
     <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Aurikularra"</string>
     <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Idazteko gailua"</string>
     <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth gailua"</string>
-    <string name="bluetooth_hearingaid_left_pairing_message" msgid="7378813500862148102">"Ezkerreko audiofonoa parekatzen…"</string>
-    <string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Eskuineko audiofonoa parekatzen…"</string>
+    <string name="bluetooth_hearingaid_left_pairing_message" msgid="7378813500862148102">"Ezkerreko audifonoa parekatzen…"</string>
+    <string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Eskuineko audifonoa parekatzen…"</string>
     <string name="bluetooth_hearingaid_left_battery_level" msgid="8797811465352097562">"Ezkerrekoa. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_hearingaid_right_battery_level" msgid="7309476148173459677">"Eskuinekoa. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Desaktibatuta dago Wi-Fi konexioa."</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android sistema eragilea"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kendutako aplikazioak"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kendutako aplikazioak eta erabiltzaileak"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Sistemaren eguneratzeak"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Konexioa partekatzea (USB)"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Sare publiko eramangarria"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Konexioa partekatzea (Bluetooth)"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 293c463..8f55092 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"دسترسی سیم‌کارت"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"‏صدای HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"‏صدای HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سمعک"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"متصل به سمعک"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سمعک‌ها"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"متصل به سمعک‌ها"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"به رسانه صوتی متصل شد"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"به تلفن صوتی متصل شد"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"به سرور انتقال فایل متصل شد"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"استفاده برای تلفن صوتی"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استفاده برای انتقال فایل"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استفاده برای چاپ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"استفاده برای سمعک"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"استفاده برای سمعک‌ها"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"مرتبط‌سازی"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"مرتبط‌سازی"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"لغو"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"‏سیستم عامل Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"برنامه‌های حذف شده"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"برنامه‌ها و کاربران حذف شده"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"به‌روزرسانی‌های سیستم"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏اتصال داده با سیم USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطه اتصال قابل حمل"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"اتصال اینترنت با تلفن همراه بلوتوث"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index f335eb9..5802a03 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kortin käyttö"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-ääni: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-ääni"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Kuulolaite"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Kuulolaite yhdistetty"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Kuulolaitteet"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Yhdistetty kuulolaitteisiin"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Yhdistetty median ääneen"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Yhdistetty puhelimen ääneen"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Yhdistetty tiedostonsiirtopalvelimeen"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Käytä puhelimen äänille"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Käytä tiedostojen siirtoon"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Käytä syöttöön"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Käytä kuulolaitteen kanssa"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Käytä kuulolaitteilla"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Muodosta laitepari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"MUODOSTA LAITEPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Peruuta"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android-käyttöjärjestelmä"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Poistetut sovellukset"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Poistetut sovellukset ja käyttäjät"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Järjestelmäpäivitykset"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Jaettu yhteys USB:n kautta"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Kannettava yhteyspiste"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jaettu Bluetooth-yhteys"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 4959103..ec8d6b0 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Prothèse auditive"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connecté à la prothèse auditive"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Prothèses auditives"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connecté aux prothèses auditives"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté à l\'audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utiliser avec la prothèse auditive"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utiliser avec les prothèses auditives"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Système d\'exploitation Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Mises à jour système"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage de connexion par USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Partage connexion Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 1f071de..0b620d8 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Assistance auditive"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connecté à la prothèse auditive"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Appareils auditifs"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connexion établie avec les appareils auditifs"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté aux paramètres audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utiliser pour l\'assistance auditive"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utiliser pour les appareils auditifs"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Plate-forme Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Mises à jour du système"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage connexion Bluetooth par USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Partage connexion Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 06faf00..b4d7fcb 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso á SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio en HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiófonos"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectouse ao audiófono"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audiófonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audiófonos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao audio multimedia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao audio do teléfono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferencia de ficheiros"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilízase para o audio do teléfono"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilízase para a transferencia de ficheiros"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilízase para a entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audiófonos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizar para audiófonos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincronizar"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacións eliminadas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacións e usuarios eliminados"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Actualizacións do sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión compart. por USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona wifi portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión por Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 232dea9..97b9036 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"સિમ ઍક્સેસ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ઑડિઓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ઑડિઓ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"સાંભળવામાં સહાય આપતું યંત્ર"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"સાંભળવામાં સહાય આપતા યંત્ર સાથે કનેક્ટ કરેલ"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"શ્રવણ યંત્રો"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"શ્રવણ યંત્રો સાથે કનેક્ટ કરેલું છે"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"મીડિયા ઑડિઓ સાથે કનેક્ટ કર્યુ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ફોન ઑડિઓ સાથે કનેક્ટ થયાં"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ફાઇલ સ્થાનાંતરણ સેવાથી કનેક્ટ થયાં"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ફોન ઑડિઓ માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ફાઇલ સ્થાનાંતર માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ઇનપુટ માટે ઉપયોગ કરો"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"સાંભળવામાં સહાય આપતા યંત્ર માટે ઉપયોગ કરો"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"શ્રવણ યંત્રો માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"જોડી"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"જોડાણ બનાવો"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"રદ કરો"</string>
@@ -136,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"દૂર કરેલી ઍપ્લિકેશનો"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"દૂર કરેલી ઍપ્લિકેશનો અને વપરાશકર્તાઓ"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ટિથરિંગ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"પોર્ટેબલ હૉટસ્પૉટ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"બ્લૂટૂથ ટિથરિંગ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 2f03eb1..8b1ca93 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडियो"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"सुनने में मददगार डिवाइस"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"सुनने में मददगार डिवाइस से जाेड़ा गया"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"सुनने में मदद करने वाले डिवाइस"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"सुनने में मदद करने वाले डिवाइस से कनेक्ट है"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडियो से कनेक्‍ट किया गया"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फ़ोन ऑडियो से कनेक्‍ट किया गया"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फ़ाइल स्‍थानांतरण सर्वर से कनेक्‍ट किया गया"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फ़ोन ऑडियो के लिए उपयोग करें"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फ़ाइल स्‍थानांतरण के लिए उपयोग करें"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट के लिए उपयोग करें"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"सुनने में मददगार डिवाइस के लिए इस्तेमाल करें"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"सुनने में मदद करने वाले डिवाइस के लिए इस्तेमाल करें"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोड़ें"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोड़ें"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द करें"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"निकाले गए ऐप्स"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ऐप्स  और उपयोगकर्ताओं को निकालें"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"सिस्टम अपडेट"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"यूएसबी से टेदरिंग"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्‍पॉट"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index bb14b06..b4cbbdb 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano sa slušnim aparatom"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparati"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezano sa Slušnim aparatima"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano s medijskim zvukom"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa telefonskim zvukom"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano s poslužiteljem za prijenos datoteka"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Koristi za telefonski zvuk"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Koristi za prijenos datoteke"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Upotrijebi za ulaz"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Upotrebljavaj za slušni aparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Upotrijebi za Slušne aparate"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Odustani"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sustava"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB dijeljenje veze"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijen. pristupna točka"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetoothom veze"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 14d8c78..589eb59 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-elérés"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hallókészülék"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Csatlakoztatva a hallókészülékhez"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hallókészülékek"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Hallókészülékhez csatlakoztatva"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Csatlakoztatva az eszköz hangjához"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Csatlakoztatva a telefon hangjához"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Csatlakozva a fájlküldő szerverhez"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Felhasználás a telefon hangjához"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Felhasználás fájlátvitelre"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Használat beviteli eszközként"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Használat hallókészülékhez"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Hallókészülékkel való használat"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párosítás"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROSÍTÁS"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Mégse"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eltávolított alkalmazások"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eltávolított alkalmazások és felhasználók"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Rendszerfrissítések"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-megosztás"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hordozható hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth megosztása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 28ebe4f..736b4c3 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM քարտի հասանելիություն"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD աուդիո՝ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD աուդիո"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Լսողական ապարատ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Միացված է լսողական ապարատին"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Լսողական ապարատ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Լսողական ապարատը միացված է"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Միացված է մեդիա աուդիոյին"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Միացված է հեռախոսի ձայնային տվյալներին"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Միացված է ֆայլերի փոխանցման սերվերին"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Օգտագործել հեռախոսի աուդիոյի համար"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Օգտագործել ֆայլի փոխանցման համար"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Օգտագործել ներմուծման համար"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Օգտագործել լսողական ապարատի համար"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Օգտագործել լսողական ապարատի համար"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Զուգավորել"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"Զուգավորել"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Չեղարկել"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Հեռացված ծրագրեր"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Հեռացված հավելվածներն ու օգտատերերը"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Համակարգի թարմացումներ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB մոդեմ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Դյուրակիր թեժ կետ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth մոդեմ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 182e523..c5d8f30 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Alat Bantu Dengar"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Terhubung ke Alat Bantu Dengar"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Alat Bantu Dengar"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Terhubung ke Alat Bantu Dengar"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tersambung ke media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tersambung ke audio ponsel"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Sambungkan ke server transfer file"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio ponsel"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk transfer file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk masukan"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gunakan untuk Alat Bantu Dengar"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gunakan untuk Alat Bantu Dengar"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sandingkan"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SANDINGKAN"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikasi dihapus"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikasi dan pengguna yang dihapus"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Update sistem"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabel"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 577b7ae..58505f5 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Aðgangur að SIM-korti"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-hljóð: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-hljóð"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Heyrnatæki"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Tengt við heyrnartæki"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Heyrnartæki"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Tengt við heyrnartæki"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tengt við hljóðspilun efnis"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tengt við hljóð símans"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Tengt við skráaflutningsþjón"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Nota fyrir hljóð símans"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Nota við skráaflutning"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Nota fyrir inntak"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Nota fyrir heyrnartæki"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Nota fyrir heyrnartæki"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Para"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PARA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hætta við"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android stýrikerfið"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjarlægð forrit"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjarlægð forrit og notendur"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Kerfisuppfærslur"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tjóðrun"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Heitur reitur"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tjóðrun"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 9fc32949..6d863aa 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accesso alla SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Apparecchio acustico"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connesso all\'apparecchio acustico"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Apparecchi acustici"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connessione con gli apparecchi acustici stabilita"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Collegato ad audio media"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Collegato ad audio telefono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Collegato al server di trasferimento file"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usa per audio telefono"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usa per trasferimento file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizza per l\'input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usa per l\'apparecchio acustico"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizza per gli apparecchi acustici"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Accoppia"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCOPPIA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annulla"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operativo Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applicazioni rimosse"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"App e utenti rimossi"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Aggiornamenti di sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portatile"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 8ee11e4..6e37c9a 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"‏גישה ל-SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"‏אודיו באיכות HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"‏אודיו באיכות HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"מכשיר שמיעה"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"מחובר למכשיר שמיעה"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"מכשירי שמיעה"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"מחובר אל מכשירי שמיעה"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"מחובר לאודיו של מדיה"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"מחובר לאודיו של הטלפון"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"מחובר לשרת העברת קבצים"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"השתמש עבור האודיו של הטלפון"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"השתמש להעברת קבצים"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"השתמש לקלט"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"יש להשתמש עבור מכשיר שמיעה"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"שימוש בשביל מכשירי שמיעה"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"התאם"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"התאם"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ביטול"</string>
@@ -136,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"אפליקציות שהוסרו"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"אפליקציות ומשתמשים שהוסרו"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏שיתוף אינטרנט דרך USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"נקודה לשיתוף אינטרנט"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"‏שיתוף אינטרנט דרך Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4efd61b..233e8e8 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIMアクセス"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD オーディオ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD オーディオ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"補聴器"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"補聴器に接続済み"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"補聴器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"補聴器に接続"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"メディアの音声に接続"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"携帯電話の音声に接続"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ファイル転送サーバーに接続"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"携帯電話の音声に使用"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ファイル転送に使用"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"入力に使用"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"補聴器に使用"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"補聴器に使用"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ペア設定する"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ペア設定する"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"キャンセル"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"削除したアプリケーション"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"削除されたアプリとユーザー"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"システム アップデート"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB テザリング"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ポータブルアクセスポイント"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth テザリング"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index c613a06b..09bea2a 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM წვდომა"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD აუდიო: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD აუდიო"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"სმენის აპარატი"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"დაკავშირებულია სმენის აპარატთან"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"სმენის მოწყობილობები"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"დაკავშირებულია სმენის მოწყობილობებთან"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"დაკავშირებულია აუდიო მულტიმედიურ სისტემასთან"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"დაკავშირებულია ტელეფონის აუდიო მოწყობილობასთან"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"დაკავშირებულია ფაილების გადაცემის სერვერთან"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"გამოიყენეთ ტელეფონის აუდიომოწყობილობაში"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ფაილების ტრანსფერისათვის გამოყენება"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"შეტანისთვის გამოყენება"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"სმენის აპარატის გამოყენება"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"გამოყენება სმენის მოწყობილობებისთვის"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"დაწყვილება"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"დაწყვილება"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"გაუქმება"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"აპების წაშლა"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"წაშლილი აპები და მომხმარებლები"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"სისტემის განახლებები"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ტეტერინგი"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"პორტატული უსადენო ქსელი"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ტეტერინგი"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 8d6d76e..8670ec9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картасына кіру"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD форматты аудиомазмұн: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD форматты аудиомазмұн"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Есту аппараты"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Есту аппаратына жалғанған"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Есту аппараттары"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Есту аппараттарына жалғанған"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиосына жалғанған"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосына қосылған"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл жіберу серверіне жалғанған"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосы үшін қолдану"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файлды жіберу үшін қолдану"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Кіріс үшін қолдану"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Есту аппаратына пайдалану"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Есту аппараттары үшін пайдалану"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жұптау"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖҰПТАУ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Бас тарту"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android операциялық жүйесі"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынған қолданбалар"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Алынған қолданбалар және пайдаланушылар"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Жүйелік жаңарту"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB тетеринг"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Алынбалы хот-спот"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9903201..6bf8a0f 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ការចូលដំណើរការស៊ីម"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"សំឡេងកម្រិត HD៖ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"សំឡេងកម្រិត HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ឧបករណ៍​ជំនួយការ​ស្ដាប់"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"បាន​ភ្ជាប់ទៅ​ឧបករណ៍​ជំនួយការ​ស្តាប់"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ឧបករណ៍​ជំនួយការ​ស្ដាប់"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"បាន​ភ្ជាប់ទៅ​ឧបករណ៍​ជំនួយការ​ស្ដាប់"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"បា​ន​ភ្ជាប់​ទៅ​អូឌីយ៉ូ​មេឌៀ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"តភ្ជាប់​ទៅ​អូឌីយ៉ូ​ទូរស័ព្ទ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"បាន​តភ្ជាប់​ទៅ​ម៉ាស៊ីន​មេ​ផ្ទេរ​ឯកសារ"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ប្រើ​សម្រាប់​​អូឌីយ៉ូ​ទូរស័ព្ទ"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ប្រើ​សម្រាប់​ផ្ទេរ​ឯកសារ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ប្រើ​សម្រាប់​បញ្ចូល"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ប្រើ​សម្រាប់​ឧបករណ៍​ជំនួយការ​ស្តាប់"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ប្រើ​សម្រាប់​ឧបករណ៍​ជំនួយការ​ស្តាប់"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ផ្គូផ្គង"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ផ្គូផ្គង"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"បោះ​បង់​"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"ប្រព័ន្ធ​ប្រតិបត្តិការ Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"កម្មវិធី​ដែល​បាន​លុប"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"បាន​លុប​កម្មវិធី និង​អ្នកប្រើ"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"បច្ចុប្បន្នភាព​ប្រព័ន្ធ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"ការ​ភ្ជាប់តាម USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ហតស្ពត​ចល័ត"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ការ​ភ្ជាប់ប៊្លូធូស"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 888b93f..41b0fbd 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ನೆಟ್‌ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ಸೈನ್ ಇನ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ಪ್ರವೇಶ ಕೇಂದ್ರ ತಾತ್ಕಾಲಿಕವಾಗಿ ಭರ್ತಿಯಾಗಿದೆ"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಲಾಗುತ್ತಿದೆ"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"ಸೈನ್-ಅಪ್ ಪೂರ್ಣಗೊಂಡಿದೆ. ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ತುಂಬಾ ನಿಧಾನವಾಗಿದೆ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ನಿಧಾನ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ಸರಿ"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ಸಿಮ್ ಪ್ರವೇಶ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ಆಡಿಯೋ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ಆಡಿಯೋ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ಶ್ರವಣ ಸಾಧನ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ಶ್ರವಣ ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ಶ್ರವಣ ಸಾಧನಗಳಿಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ಮಾಧ್ಯಮ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ಫೋನ್ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ಫೈಲ್ ವರ್ಗಾವಣೆ ಸರ್ವರ್‌ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ಫೋನ್‌ ಆಡಿಯೋಗಾಗಿ ಬಳಕೆ"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ಫೈಲ್‌ ವರ್ಗಾವಣೆಗಾಗಿ ಬಳಸು"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ಇನ್‌ಪುಟ್‌ಗಾಗಿ ಬಳಸು"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ಶ್ರವಣ ಸಾಧನಕ್ಕಾಗಿ ಬಳಸಿ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ಜೋಡಿ"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ಜೋಡಿ ಮಾಡು"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ರದ್ದುಮಾಡಿ"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ತೆಗೆದುಹಾಕಲಾದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ಟೆಥರಿಂಗ್"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ಪೋರ್ಟಬಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ಬ್ಲೂಟೂತ್‌‌ ಟೆಥರಿಂಗ್‌"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d7e9488..877866c 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 액세스"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 오디오: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 오디오"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"보청기"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"보청기에 연결됨"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"보청기"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"보청기에 연결됨"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"미디어 오디오에 연결됨"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"휴대전화 오디오에 연결됨"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"파일 전송 서버에 연결됨"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"휴대전화 오디오에 사용"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"파일 전송에 사용"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"입력에 사용"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"보청기에 사용"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"보청기로 사용"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"페어링"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"페어링"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"취소"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"삭제된 앱"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"삭제된 앱 및 사용자"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"시스템 업데이트"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 테더링"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"휴대용 핫스팟"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"블루투스 테더링"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 6898c61..9e4a9a8 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD форматындагы аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD форматындагы аудио"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Угуу аппараты"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Угуу аппаратына туташты"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Угуу аппараттары"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Угуу аппараттарына туташып турат"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиого туташты"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосуна туташты"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл өткөрүү серверине туташты"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосу үчүн колдонулсун"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл өткөрүү үчүн колдонулсун"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Киргизүү үчүн колдонулсун"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Угуу аппараты үчүн колдонуу"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Угуу аппараттары үчүн колдонуу"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жупташтыруу"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖУПТАШТЫРУУ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Жок"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынып салынган колдонмолор"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Өчүрүлгөн колдонмолор жана колдонуучулар"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Тутум жаңыртуулары"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ташыма кошулуу чекити"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index acbaf70..7e6aaac 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ການ​ເຂົ້າ​ເຖິງ SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"ສຽງ HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"ສຽງ HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ເຄື່ອງຊ່ວຍຟັງ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ເຊື່ອມຕໍ່ຫາເຄື່ອງຊ່ວຍຟັງແລ້ວ"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ອຸປະກອນຊ່ວຍຟັງ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ເຊື່ອມຕໍ່ຫາອຸປະກອນຊ່ວຍຟັງແລ້ວ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ເຊື່ອມຕໍ່ກັບສຽງໂທລະສັບແລ້ວ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ເຊື່ອມຕໍ່ກັບເຊີບເວີໂອນຍ້າຍໄຟລ໌ແລ້ວ"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ໃຊ້ສຳລັບລະບົບສຽງຂອງໂທລະສັບ"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ໃຊ້ເພື່ອໂອນຍ້າຍໄຟລ໌"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ໃຊ້ສຳລັບການປ້ອນຂໍ້ມູນ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ໃຊ້ກັບເຄື່ອງຊ່ວຍຟັງ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ໃຊ້ສຳລັບອຸປະກອນຊ່ວຍຟັງ"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ຈັບຄູ່"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ຈັບຄູ່"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ຍົກເລີກ"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ແອັບຯທີ່ຖືກລຶບອອກແລ້ວ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ລຶບແອັບຯ ແລະຜູ່ໃຊ້ແລ້ວ"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"ການອັບເດດລະບົບ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"ການປ່ອຍສັນຍານຜ່ານ USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ຮັອດສະປອດເຄື່ອນທີ່"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ປ່ອຍສັນຍານຜ່ານ Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8933451..71b8cf0 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM prieiga"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD garsas: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD garsas"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Klausos aparatas"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Prisijungta prie klausos aparato"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Klausos aparatai"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Prisijungta prie klausos aparatų"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Prijungta prie medijos garso įrašo"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Prijungta prie telefono garso"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Prijungta prie failų perkėlimo serverio"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Naudoti telefono garso įrašui"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Naudoti failų perkėlimui"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Naudoti įvedant"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Klausos aparato naudojimas"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Naudoti su klausos aparatais"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Susieti"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SUSIETI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atšaukti"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"„Android“ OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Pašalintos programos"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Pašalintos programos ir naudotojai"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Sistemos naujiniai"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB susiejimas"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Perkeliama aktyvioji sritis"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"„Bluetooth“ susiejimas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index ac41ae0..82ec0ae 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Piekļuve SIM kartei"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Dzirdes aparāts"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Izveidots savienojums ar dzirdes aparātu"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Dzirdes aparāti"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Izveidots savienojums ar dzirdes aparātiem"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Savienots ar multivides audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Savienots ar tālruņa audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Savienots ar failu pārsūtīšanas serveri"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Izmantot tālruņa skaņai"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Izmantot faila pārsūtīšanai"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Izmantot ievadei"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Izmantot dzirdes aparātam"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Izmantot dzirdes aparātiem"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Izveidot pāri"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SAVIENOT PĀRĪ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atcelt"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Noņemtās lietotnes"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Noņemtās lietotnes un lietotāji"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Sistēmas atjauninājumi"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB saistīšana"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Pārnēsājams tīklājs"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth saistīšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 5b45977..c99ef68 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Пристап до SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD аудио"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слушно помагало"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Поврзано со слушно помагало"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слушни помагала"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Поврзано со слушни помагала"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Поврзан со аудио на медиуми"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Поврзан со аудио на телефон"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Поврзан со сервер за пренос на датотеки"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Користи за аудио на телефон"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Користи за пренос на датотеки"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за внес"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Користете како слушно помагало"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Користи за слушни помагала"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Спари"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СПАРИ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Оперативен систем Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Отстранети апликации"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Отстранети апликации и корисници"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ажурирања на системот"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Поврзување со USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносл. точка на пристап"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Поврзување со Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index efe6cd6..0292cab 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ആക്സസ്"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ഓഡിയോ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ഓഡിയോ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ശ്രവണ സഹായി"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ശ്രവണ സഹായിലേക്ക് കണക്റ്റ് ചെയ്‌‌തു"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ശ്രവണ സഹായികൾ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ശ്രവണ സഹായികളിലേക്ക് കണക്‌റ്റ് ചെയ്‌തു"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"മീഡിയ ഓഡിയോയിലേക്ക് കണ‌ക്റ്റുചെയ്‌തു"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ഫോൺ ഓഡിയോയിൽ കണ‌ക്റ്റുചെ‌യ്‌തു"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ഫയൽ കൈമാറ്റ സെർവറിലേക്ക് കണ‌ക്റ്റുചെ‌യ്‌തു"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ഫോൺ ഓഡിയോയ്ക്കായി ഉപയോഗിക്കുക"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ഫയൽ കൈമാറ്റത്തിനായി ഉപയോഗിക്കുന്നു"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ഇൻപുട്ടിനായി ഉപയോഗിക്കുക"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ശ്രവണ സഹായത്തിനായി ഉപയോഗിക്കുക"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ശ്രവണ സഹായികൾക്കായി ഉപയോഗിക്കുക"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ജോടിയാക്കുക"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ജോടിയാക്കുക"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"റദ്ദാക്കുക"</string>
@@ -136,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"നീക്കംചെയ്‌ത അപ്ലിക്കേഷനുകൾ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"നീക്കംചെയ്‌ത അപ്ലിക്കേഷനുകളും ഉപയോക്താക്കളും"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ടെതറിംഗ്"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"പോർട്ടബിൾ ഹോട്ട്സ്‌പോട്ട്"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 9c3db2e..10c1415 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -49,8 +49,8 @@
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s-р боломжтой"</string>
     <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>-г нээж байна"</string>
     <string name="osu_connect_failed" msgid="2187750899158158934">"Холбогдож чадсангүй"</string>
-    <string name="osu_completing_sign_up" msgid="9037638564719197082">"Бүртгүүлэлтийг дуусгаж байна…"</string>
-    <string name="osu_sign_up_failed" msgid="7296159750352873260">"Бүртгүүлэлтийг дуусгаж чадсангүй. Дахин оролдохын тулд товшино уу."</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"Бүртгэлийг дуусгаж байна…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"Бүртгэлийг дуусгаж чадсангүй. Дахин оролдохын тулд товшино уу."</string>
     <string name="osu_sign_up_complete" msgid="8207626049093289203">"Бүртгүүлж дууслаа. Холбогдож байна…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Маш удаан"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Удаан"</string>
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Хандалт"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD аудио"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Сонсголын төхөөрөмж"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Сонсголын төхөөрөмжид холбогдсон"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Сонсголын төхөөрөмж"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Сонсголын төхөөрөмжтэй холбосон"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиод холбогдсон"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Утасны аудид холбогдсон"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл дамжуулах серверт холбогдсон"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Утасны аудиод ашиглах"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл дамжуулахад ашиглах"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Оруулахад ашиглах"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Сонсголын төхөөрөмжид ашиглах"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Сонсголын төхөөрөмжид ашиглах"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Хослуулах"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ХОСЛУУЛАХ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Цуцлах"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Андройд OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Арилгасан апп-ууд"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Арилгасан апп-ууд болон хэрэглэгчид"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Системийн шинэчлэлт"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем болгох"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Зөөврийн сүлжээний цэг"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем болгох"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index a792c4f..0c2f0ee 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम प्रवेश"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडिओ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडिओ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ऐकण्याची सुविधा"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ऐकण्याच्या सुविधेशी कनेक्ट केलेले आहे"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"श्रवण यंत्रे"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडिओवर कनेक्ट केले"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन ऑडिओ वर कनेक्ट केले"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन ऑडिओसाठी वापरा"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाईल स्थानांतरणासाठी वापरा"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट साठी वापरा"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ऐकण्याच्या सुविधेसाठी वापरा"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"श्रवण यंत्रांसाठी वापरा"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"पेअर करा"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"पेअर करा"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द करा"</string>
@@ -136,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"काढलेले अॅप्स"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"काढलेले अॅप्स आणि वापरकर्ते"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 419c6ec..e98418d 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Alat Bantu Pendengaran"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Disambungkan ke Alat Bantu Pendengaran"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Alat Bantu Dengar"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Disambungkan pada Alat Bantu Dengar"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Disambungkan ke audio media"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Disambungkan ke audio telefon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Bersambung ke pelayan pemindahan fail"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio telefon"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk pemindahan fail"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gunakan untuk Alat Bantu Pendengaran"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gunakan untuk Alat Bantu Dengar"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Jadikan pasangan"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"JADIKAN PASANGAN"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apl dialih keluar"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apl dan pengguna yang dialih keluar"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Kemas kini sistem"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Penambatan USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Titik panas mudah alih"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Penambatan Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3e9e181..ed2aae6 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM အသုံးပြုခြင်း"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD အသံ- <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD အသံ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"နားကြားကိရိယာ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"နားကြားကိရိယာသို့ ချိတ်ဆက်ထားသည်"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"နားကြားကိရိယာ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"နားကြားကိရိယာနှင့် ချိတ်ဆက်ပြီးပါပြီ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"မီဒီယာအသံအား ချိတ်ဆက်ရန်"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ဖုန်းအသံအား ချိတ်ဆက်ရန်"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ဖိုင်လွှဲပြောင်းမည့်ဆာဗာနှင့် ချိတ်ဆက်ထားပြီး"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ဖုန်းအသံအားအသုံးပြုရန်"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ဖိုင်လွဲပြောင်းရန်အတွက်အသုံးပြုရန်"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ထည့်သွင်းရန်အသုံးပြုသည်"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"နားကြားကိရိယာအတွက် အသုံးပြုရန်"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"နားကြားကိရိယာအတွက် အသုံးပြုသည်"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"အတူတွဲပါ"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ချိတ်တွဲရန်"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"မလုပ်တော့"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ဖယ်ရှားထားသော အက်ပ်များ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ဖယ်ရှားထားသော အပလီကေးရှင်းနှင့် သုံးစွဲသူများ"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"စနစ် အပ်ဒိတ်လုပ်ခြင်းများ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB သုံး၍ချိတ်ဆက်ခြင်း"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ရွေ့လျားနိုင်သောဟော့စပေါ့"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ဘလူးတုသ်သုံးချိတ်ဆက်ခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 65085e8..93e53e5 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Tilgang til SIM-kortet"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-lyd"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Høreapparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Koblet til høreapparat"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Høreapparater"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Koblet til høreapparater"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Koblet til medielyd"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Koblet til telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Koblet til tjener for filoverføring"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Bruk for telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Bruk til filoverføring"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Bruk for inndata"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Bruk for høreapparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Bruk for høreapparater"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sammenkoble"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOBLE TIL"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android-operativsystem"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apper"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apper og brukere"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Systemoppdateringer"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-internettdeling"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Flyttbar trådløs sone"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-internettdeling"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 16b3b07..7daf956 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s मार्फत् स्वतः जडान गरिएको"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s मार्फत जडित"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"जडान गरियो तर इन्टरनेट छैन"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"इन्टरनेटमाथिको पहुँच छैन"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन गर्न आवश्यक छ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"पहुँचसम्बन्धी स्थान अस्थायी रूपमा भरिएको छ"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s मार्फत जडान गरियो"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s मार्फत उपलब्ध"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोल्दै"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"जडान गर्न सकिएन"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"साइन अप गर्ने कार्य सम्पन्न गर्दै…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"साइन अप गर्ने कार्य सम्पन्न गर्न सकिएन। फेरि प्रयास गर्न ट्याप गर्नुहोस्।"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"साइन अप गर्ने कार्य सम्पन्न भयो। जडान गर्दै…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"धेरै ढिलो"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"बिस्तारै"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ठिक छ"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM पहुँच"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD अडियो"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"सुन्नमा मद्दत गर्ने यन्त्र"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"सुन्नमा मद्दत गर्ने यन्त्रमा जडान गरियो"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"श्रवण यन्त्रहरू"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"श्रवण यन्त्रहरूमा जडान गरियो"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मिडिया अडियोसँग जडित"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन अडियोमा जडान गरियो"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाइल ट्रान्सफर सर्भरमा जडान गरियो"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन अडियोको लागि प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुटको लागि प्रयोग गर्नुहोस्"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"सुन्नमा मद्दत गर्ने यन्त्रका लागि प्रयोग गर्नुहोस्"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोडी"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोडी"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द गर्नुहोस्"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"एन्ड्रोइड OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"हटाइएका अनुप्रयोगहरू"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"अनुप्रयोगहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेथर गर्दै"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हटस्पट"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लुटुथ टेथर गर्दै"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 2f8c2c2..959c1ea 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Sim-toegang"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Gehoorapparaat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Verbonden met gehoorapparaat"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Gehoorapparaten"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Verbonden met gehoorapparaten"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbonden met audio van medium"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbonden met audio van telefoon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Verbonden met server voor bestandsoverdracht"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruiken voor audio van telefoon"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruiken voor bestandsoverdracht"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruiken voor invoer"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gebruiken voor gehoorapparaat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gebruiken voor gehoorapparaten"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppelen"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELEN"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuleren"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android-besturingssysteem"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwijderde apps"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwijderde apps en gebruikers"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Systeemupdates"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Draagbare hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tethering"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 95ab7bb..54ddf84 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍‌ନେଟ୍‌ ନାହିଁ"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ ନାହିଁ"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ସାଇନ୍-ଇନ୍ ଆବଶ୍ୟକ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ଆକ୍ସେସ୍ ପଏଣ୍ଟ ସାମୟିକ ଭାବେ ପୂର୍ଣ୍ଣ"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ଖୋଲୁଛି"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"ସଂଯୋଗ କରିହେଲା ନାହିଁ"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"ସାଇନ୍ ଅପ୍ ଶେଷ ହେଉଛି…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇପାରିଲା ନାହିଁ। ପୁଣି ଥରେ ଚେଷ୍ଟା କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇଛି। ସଂଯୋଗ କରୁଛି…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ବହୁତ ମନ୍ଥର"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"କମ୍‌ ବେଗ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ଠିକ୍‌ ଅଛି"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ସିମ୍‌ ଆକ୍ସେସ୍‌"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ଅଡିଓ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ଶ୍ରବଣ ଯନ୍ତ୍ର ସହିତ ସଂଯୁକ୍ତ ହେଲା"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ଶ୍ରବଣ ଯନ୍ତ୍ରକୁ ସଂଯୋଗ ହୋଇଛି"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ମିଡିଆ ଅଡିଓ ସହ ସଂଯୁକ୍ତ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ଫୋନ୍‌ ଅଡିଓ ସହିତ ସଂଯୁକ୍ତ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ ସର୍ଭର୍‌ ସହ ସଂଯୁକ୍ତ"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ଫୋନ୍‌ ଅଡିଓ ପାଇଁ ବ୍ୟବହାର କର"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ଇନ୍‌ପୁଟ୍‌ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ପେୟାର୍‌"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ପେୟାର୍‌"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"କ୍ୟାନ୍ସଲ୍‌ କରନ୍ତୁ"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"କଢ଼ାଯାଇଥିବା ଆପ୍‌ଗୁଡ଼ିକ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ଆପ୍‌ ଏବଂ ଉପଯୋଗକର୍ତ୍ତା ବାହାର କରାଗଲା"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ଟିଥରିଙ୍ଗ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ପୋର୍ଟବଲ୍‌ ହଟସ୍ପଟ୍‌"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ବ୍ଲୁଟୂଥ ଟିଥରିଙ୍ଗ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 1ddd862..dcf3e3c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ਸਾਈਨ-ਇਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ਐਕਸੈੱਸ ਪੁਆਇੰਟ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਸੰਪੂਰਨ ਰੁਝੇਂਵੇਂ ਵਿੱਚ ਹੈ"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਹੋਇਆ ਕਨੈਕਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ਬਹੁਤ ਹੌਲੀ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ਹੌਲੀ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ਠੀਕ ਹੈ"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ਸਿਮ ਪਹੁੰਚ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ਆਡੀਓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ਆਡੀਓ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ਸੁਣਨ ਦਾ ਸਾਧਨ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ਸੁਣਨ ਦੇ ਸਾਧਨ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ਮੀਡੀਆ  ਆਡੀਓ  ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ਫ਼ੋਨ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ਫਾਈਲ ਟ੍ਰਾਂਸਫ਼ਰ ਸਰਵਰ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ਫ਼ੋਨ ਔਡੀਓ ਲਈ ਵਰਤੋ"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਲਈ ਵਰਤੋ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ਇਨਪੁਟ ਲਈ ਵਰਤੋ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ਸੁਣਨ ਦੇ ਸਾਧਨ ਲਈ ਵਰਤੋ"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਲਈ ਵਰਤੋ"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ਪੇਅਰ ਕਰੋ"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ਪੇਅਰ ਕਰੋ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ਰੱਦ ਕਰੋ"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੈਦਰਿੰਗ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ਬਲੂਟੁੱਥ ਟੈਦਰਿੰਗ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e2109c8..505e395 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostęp do karty SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Dźwięk HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Dźwięk HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparat słuchowy"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Po połączeniu z aparatem słuchowym"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparaty słuchowe"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Połączono z aparatami słuchowymi"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Połączono z funkcją audio multimediów"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Połączono z funkcją audio telefonu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Połączono z serwerem transferu plików"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Użyj dla funkcji audio telefonu"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Użyj do transferu plików"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Użyj do wprowadzania"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Użyj dla funkcji aparatu słuchowego"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Użyj z aparatami słuchowymi"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sparuj"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SPARUJ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anuluj"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"System operacyjny Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Usunięte aplikacje"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usunięte aplikacje i użytkownicy"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizacje systemu"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering przez USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Przenośny punkt dostępu"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering przez Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 19e22e420..c0f31a6 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a um aparelho auditivo"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a aparelhos auditivos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para aparelho auditivo"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para aparelhos auditivos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 1f1072c..08c3cc8 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Associar ao aparelho auditivo"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Ligado a aparelhos auditivos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ligado ao áudio de multimédia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ligado ao áudio do telefone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ligado ao servidor de transferência de ficheiros"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para áudio do telefone"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para transferência de ficheiros"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilizar com o aparelho auditivo"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizar para aparelhos auditivos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincr."</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicações removidas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicações e utilizadores removidos"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ligação USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ligação Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 19e22e420..c0f31a6 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a um aparelho auditivo"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a aparelhos auditivos"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para aparelho auditivo"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para aparelhos auditivos"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 7fa75a8..015eb92 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acces la SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparat auditiv"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectat la aparatul auditiv"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparate auditive"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectat la aparatul auditiv"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectat la profilul pentru conținut media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectat la componenta audio a telefonului"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectat la serverul de transfer de fișiere"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizați pentru componenta audio a telefonului"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizați pentru transferul de fișiere"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizați pentru introducere date"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilizați pentru aparatul auditiv"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Folosiți pentru aparatele auditive"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Asociați"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CONECTAȚI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulați"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Sistem de operare Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicații eliminate"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicații și utilizatori eliminați"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Actualizări de sistem"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering prin USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering prin Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 6cdfd3c..b0b592c 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ к SIM-карте"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD Audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слуховой аппарат"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Подключен к слуховому аппарату"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слуховые аппараты"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Слуховой аппарат подключен"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Подключено к мультимедийному аудиоустройству"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Подключено к аудиоустройству телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установлено подключение к серверу передачи файлов"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Использовать для аудиоустройства телефона"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Используется для передачи файлов"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Использовать для ввода"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Использовать для слухового аппарата"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Использовать для слухового аппарата"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Добавить"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ДОБАВИТЬ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отмена"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Удаленные приложения"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Удаленные приложения и пользователи"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Обновления системы"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-модем"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Точка доступа Wi-Fi"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-модем"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index f85289c..ca7a962 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ප්‍රවේශය"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ශ්‍රව්‍යය: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ශ්‍රව්‍යය"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ශ්‍රවණාධාරකය"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ශ්‍රවණාධාරකයට සම්බන්ධයි"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ශ්‍රවණාධාරක"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ශ්‍රවණාධාරක වෙත සම්බන්ධ කළා"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"මාධ්‍ය ශ්‍රව්‍යට සම්බන්ධ විය"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"දුරකතනයේ ශ්‍රව්‍යට සම්බන්ධ විය"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ගොනු හුවමාරු සේවාදායකය සමග සම්බන්ධ විය"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"දුරකථන ශ්‍රව්‍ය සඳහා භාවිතා කෙරේ"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ගොනු හුවමාරුව සඳහා භාවිතා කරන්න"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ආදානය සඳහා භාවිතා කරන්න"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ශ්‍රවණාධාරකය සඳහා භාවිත කරන්න"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ශ්‍රවණාධාර සඳහා භාවිත කරන්න"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"යුගල කරන්න"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"යුගල කරන්න"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"අවලංගු කරන්න"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ඉවත් කළ යෙදුම්"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"යෙදුම් සහ පරිශීලකයින් ඉවත් කරන ලදි"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"පද්ධති යාවත්කාලීන"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ටෙදරින්"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ජංගම හොට්ස්පොට්"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"බ්ලූටූත් ටෙදරින්"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index eae5be7..dd7efdd 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Prístup k SIM karte"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Načúvacia pomôcka"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Pripojené k načúvacej pomôcke"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Načúvacie pomôcky"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Pripojené k načúvacím pomôckam"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Pripojené ku zvukovému médiu"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Pripojené ku zvuku telefónu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Pripojené na server pre prenos údajov"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Použiť pre zvuk telefónu"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použiť na prenos súborov"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použiť pre vstup"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Použitie načúvacej pomôcky"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použiť pre načúvacie pomôcky"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovať"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAŤ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušiť"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstránené aplikácie"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstránené aplikácie a používatelia"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizácie systému"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pripojenie cez USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosný prístupový bod"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pripojenie cez Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index bef9a3e..fada686 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostop do kartice SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Zvok visoke kakovosti: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Zvok visoke kakovosti"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni pripomoček"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezava s slušnim pripomočkom je vzpostavljena"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni pripomočki"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezava s slušnimi pripomočki je vzpostavljena"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezan s profilom za predstavnostni zvok"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezava s profilom za zvok telefona vzpostavljena"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezava s strežnikom za prenos datotek je vzpostavljena"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Uporabi za zvok telefona"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uporabi za prenos datotek"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Uporabi za vnos"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Uporaba za slušni pripomoček"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Uporabi za slušne pripomočke"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seznani"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEZNANI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Prekliči"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstranjene aplikacije"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstranjene aplikacije in uporabniki"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Posodobitve sistema"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internet prek USB-ja"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosna dostopna točka"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Internet prek Bluetootha"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 8bb67f9..2132767 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Qasje në kartën SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparati i dëgjimit"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Lidhur me aparatin e dëgjimit"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparatet e dëgjimit"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Lidhur me aparatet e dëgjimit"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"U lidh me audion e medias"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"U lidh me audion e telefonit"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"U lidh me serverin e transferimit të skedarëve"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Përdor për audion e telefonit"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Përdor për transferimin e skedarëve"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Përdore për hyrjen"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Përdore për aparatin e dëgjimit"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Përdore për aparatet e dëgjimit"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Çifto"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ÇIFTO"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulo"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Sistemi operativ Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikacionet e hequra"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikacionet dhe përdoruesit e hequr"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Përditësimet e sistemit"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ndarje përmes USB-së"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona e qasjes e lëvizshme"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ndarje interneti përmes Bluetooth-it"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4378a46..6cf4b20 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Приступ SIM картици"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD звук: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD звук"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слушни апарат"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Повезано са слушним апаратом"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слушни апарати"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Повезано са слушним апаратима"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Повезано са звуком медија"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Повезано са звуком телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Повезано са сервером за пренос датотека"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Коришћење за аудио телефона"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Коришћење за пренос датотека"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за улаз"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Користи за слушни апарат"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Користи за слушне апарате"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Упари"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"УПАРИ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android ОС"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Уклоњене апликације"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Уклоњене апликације и корисници"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Ажурирања система"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Интернет повезивање"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносни хотспот"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth привезивање"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 3fcb6dc..f2950dc 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-åtkomst"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-ljud: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-ljud"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hörapparat"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Ansluten till hörapparat"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hörapparater"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Ansluten till hörapparater"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ansluten till medialjud"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ansluten till telefonens ljud"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ansluten till filöverföringsserver"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Använd för telefonens ljud"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Använd för filöverföring"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Använd för inmatning"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Använd med hörapparat"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Använd med hörapparater"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parkoppling"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PARKOPPLA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Operativsystemet Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Borttagna appar"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Borttagna appar och användare"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Systemuppdateringar"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internetdelning via USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobil surfzon"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Delning via Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 47c7bb2..d8a74a1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ufikiaji wa SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Sauti ya HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Sauti ya HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Visaidizi vya Kusikia"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Imeunganishwa kwenye Visaidizi vya Kusikia"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Vifaa vya Kusaidia Kusikia"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Imeunganishwa kwenye Vifaa vya Kusaidia Kusikia"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Imeunganishwa kwenye sikika ya njia ya mawasiliano"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Imeunganishwa kwenye sauti ya simu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Imeunganishwa kwenye seva ya kuhamisha faili"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Tumia kwa sauti ya simu"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Tumia kwa hali faili"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Tumia kwa kuingiza"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Tumia katika Visaidizi vya Kusikia"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Tumia kwenye Vifaa vya Kusaidia Kusikia"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Oanisha"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"OANISHA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ghairi"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"OS ya Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Programu zilizoondolewa"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Watumiaji na programu ziilizoondolewa"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Masasisho ya mfumo"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Shiriki intaneti kwa USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Intaneti ya kusambazwa"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Shiriki intaneti kwa Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 63c6cd9..71716ce 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s வழியாக இணைக்கப்பட்டது"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"பதிவு செய்யத் தட்டவும்"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"இணைய இணைப்பு இல்லை"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"உள்நுழைய வேண்டும்"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"தற்காலிகமாக அணுகல் புள்ளி நிரம்பியுள்ளது"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s வழியாக இணைக்கப்பட்டது"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s வழியாகக் கிடைக்கிறது"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> திறக்கப்படுகிறது"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"இணைக்க முடியவில்லை"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"பதிவு செய்வது நிறைவடைகிறது…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"பதிவு செய்வதை நிறைவுசெய்ய இயலவில்லை மீண்டும் முயற்சிக்கத் தட்டவும்."</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"பதிவு செய்வது நிறைவடைந்தது. இணைக்கிறது…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"மிகவும் வேகம் குறைவானது"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"வேகம் குறைவு"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"சரி"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"சிம் அணுகல்"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ஆடியோ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ஆடியோ"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"செவித்துணைக் கருவி"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"செவித்துணைக் கருவியுடன் இணைக்கப்பட்டிருக்கிறது"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"செவித்துணை கருவிகள்"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"செவித்துணை கருவிகளுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"கோப்பைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"கோப்பு பரிமாற்றத்திற்காகப் பயன்படுத்து"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"உள்ளீட்டுக்குப் பயன்படுத்து"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"செவித்துணைக் கருவிக்காகப் பயன்படுத்து"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"இணை"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"இணை"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ரத்துசெய்"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"புளூடூத் டெதெரிங்"</string>
@@ -203,7 +198,7 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"இவரால் VPN அமைப்புகளை மாற்ற முடியாது"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"இவரால் இணைப்புமுறை அமைப்புகளை மாற்ற முடியாது"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"இவரால் ஆக்சஸ் பாயிண்ட் நேம் அமைப்புகளை மாற்ற முடியாது"</string>
-    <string name="enable_adb" msgid="7982306934419797485">"USB பிழைத்திருத்தம்"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"USB பிழைதிருத்தம்"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"USB இணைக்கப்பட்டிருக்கும்போது பிழைத்திருத்தப் பயன்முறையை அமை"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"USB பிழைத்திருத்த அங்கீகரிப்புகளை நிராகரி"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"பிழைப் புகாருக்கான ஷார்ட்கட்"</string>
@@ -263,8 +258,8 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"காட்சி பண்புக்கூறு சோதனையை இயக்கு"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"வைஃபை இயங்கும் போதும் (வேகமான நெட்வொர்க் மாற்றத்திற்கு), மொபைல் டேட்டாவை எப்போதும் இயக்கத்தில் வைக்கும்."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை கிடைக்கும் போது, அதைப் பயன்படுத்தும்"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைத்திருத்தத்தை அனுமதிக்கவா?"</string>
-    <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைத்திருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைதிருத்தத்தை அனுமதிக்கவா?"</string>
+    <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைதிருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"நீங்கள் ஏற்கனவே அனுமதித்த எல்லா கணினிகளிலிருந்தும் USB பிழைத்திருத்தத்திற்கான அணுகலைத் திரும்பப்பெற வேண்டுமா?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"மேம்பட்ட அமைப்புகளை அனுமதிக்கவா?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"இந்த அமைப்பு மேம்பட்டப் பயன்பாட்டிற்காக மட்டுமே. உங்கள் சாதனம் மற்றும் அதில் உள்ள பயன்பாடுகளைச் சிதைக்கும் அல்லது தவறாகச் செயல்படும் வகையில் பாதிப்பை ஏற்படுத்தும்."</string>
@@ -276,10 +271,10 @@
     <string name="enable_terminal_summary" msgid="67667852659359206">"அக ஷெல் அணுகலை வழங்கும் இறுதிப் பயன்பாட்டை இயக்கு"</string>
     <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP சரிபார்ப்பு"</string>
     <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP சரிபார்க்கும் செயல்பாடுகளை அமை"</string>
-    <string name="debug_debugging_category" msgid="6781250159513471316">"பிழைத்திருத்தம்"</string>
+    <string name="debug_debugging_category" msgid="6781250159513471316">"பிழைதிருத்தம்"</string>
     <string name="debug_app" msgid="8349591734751384446">"பிழைத்திருத்தப் பயன்பாட்டைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="debug_app_not_set" msgid="718752499586403499">"பிழைத்திருத்தப் பயன்பாடு அமைக்கப்படவில்லை"</string>
-    <string name="debug_app_set" msgid="2063077997870280017">"பிழைத்திருத்தும் பயன்பாடு: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="debug_app_set" msgid="2063077997870280017">"பிழைதிருத்தும் பயன்பாடு: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="select_application" msgid="5156029161289091703">"பயன்பாட்டைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="no_application" msgid="2813387563129153880">"ஒன்றுமில்லை"</string>
     <string name="wait_for_debugger" msgid="1202370874528893091">"பிழைதிருத்திக்குக் காத்திருக்கவும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index d8ec167..e4dd85f 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM యాక్సెస్"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ఆడియో"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"వినికిడి పరికరం"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"వినికిడి పరికరానికి కనెక్ట్ చేస్తోంది"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"వినికిడి మద్దతు ఉపకరణాలు"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"వినికిడి మద్దతు ఉపకరణాలకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"మీడియా ఆడియోకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ఫోన్ ఆడియోకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ఫైల్ బదిలీ సర్వర్‌కు కనెక్ట్ చేయబడింది"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ఫోన్ ఆడియో కోసం ఉపయోగించు"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ఫైల్ బదిలీ కోసం ఉపయోగించు"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ఇన్‌పుట్ కోసం ఉపయోగించు"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"వినికిడి పరికరం కోసం ఉపయోగించు"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"వినికిడి మద్దతు ఉపకరణాలకు ఉపయోగించండి"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"జత చేయి"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"జత చేయి"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"రద్దు చేయి"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"తీసివేయబడిన అనువర్తనాలు"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"తీసివేయబడిన అనువర్తనాలు మరియు వినియోగదారులు"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్‌డేట్‌లు"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్‌స్పాట్"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 1f4c727..cf8362f 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"การเข้าถึงซิม"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"เสียง HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"เสียง HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"เครื่องช่วยการได้ยิน"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"เชื่อมต่อเครื่องช่วยการได้ยินแล้ว"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"เครื่องช่วยการได้ยิน"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"เชื่อมต่อกับเครื่องช่วยการได้ยินแล้ว"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"เชื่อมต่อกับระบบเสียงของสื่อแล้ว"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"เชื่อมต่อกับระบบเสียงของโทรศัพท์แล้ว"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"เชื่อมต่อกับเซิร์ฟเวอร์สำหรับโอนไฟล์แล้ว"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ใช้สำหรับระบบเสียงของโทรศัพท์"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ใช้สำหรับการโอนไฟล์"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ใช้สำหรับการป้อนข้อมูล"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ใช้สำหรับเครื่องช่วยการได้ยิน"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ใช้สำหรับเครื่องช่วยการได้ยิน"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"จับคู่อุปกรณ์"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"จับคู่อุปกรณ์"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ยกเลิก"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"ระบบปฏิบัติการของ Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"แอปพลิเคชันที่นำออก"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"การอัปเดตระบบ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"ปล่อยสัญญาณผ่าน USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ฮอตสปอตแบบพกพาได้"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ปล่อยสัญญาณบลูทูธ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 7a31cc9e..e690800 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Access sa SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Nakakonekta sa Hearing Aid"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Mga Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Nakakonekta sa Mga Hearing Aid"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Konektado sa media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Nakakonekta sa audio ng telepono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Nakakonekta sa server sa paglilipat ng file"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Ginagamit para sa audio ng telepono"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Ginagamit para sa paglilipat ng file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gamitin para sa input"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gamitin para sa Hearing Aid"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gamitin para sa Mga Hearing Aid"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pares"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"IPARES"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselahin"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Mga inalis na app"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Mga inalis na apps at user"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Mga pag-update ng system"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pag-tether sa USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable na hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pag-tether ng Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 28fc38f..ea340e8 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Erişimi"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ses: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ses"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"İşitme Cihazı"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"İşitme Cihazına bağlandı"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"İşitme Cihazları"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"İşitme Cihazlarına Bağlandı"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Medya sesine bağlanıldı"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon sesine bağlandı"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Dosya aktarım sunucusuna bağlandı"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon sesi için kullan"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Dosya aktarımı için kullan"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Giriş için kullan"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"İşitme Cihazı için kullan"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"İşitme Cihazları için kullan"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Eşle"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"EŞLE"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"İptal"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kaldırılan uygulamalar"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kaldırılmış kullanıcılar ve uygulamalar"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Sistem güncellemeleri"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Taşınabilir hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index c63f7e0..cf90ed1 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ до SIM-карти"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-аудіо: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-аудіо"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слуховий апарат"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Під’єднано до слухового апарата"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слухові апарати"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Підключено до слухових апаратів"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Підключено до аудіоджерела"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Підключено до звуку телеф."</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Підключ. до сервера передачі файлів"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Викор. для звуку тел."</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Викор. для перед. файлів"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Викор. для введ."</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Використовувати для слухового апарата"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Використовувати для слухових апаратів"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Підключити"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ПІДКЛЮЧИТИСЯ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Скасувати"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Видалені програми"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Видалені програми та користувачі"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Оновлення системи"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-модем"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Порт. точка дост."</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-модем"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 27aa730..65ed110 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -38,27 +38,20 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"‏‎%1$s کے ذریعے از خود منسلک کردہ"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"‏منسلک بذریعہ ‎%1$s"</string>
-    <!-- no translation found for connected_via_app (5571999941988929520) -->
-    <skip />
+    <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"‏دستیاب بذریعہ ‎%1$s"</string>
-    <!-- no translation found for tap_to_sign_up (6449724763052579434) -->
-    <skip />
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"سائن اپ کے لیے تھپتھپائیں"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"منسلک، انٹرنیٹ نہیں ہے"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"انٹرنیٹ نہیں ہے"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"سائن ان درکار ہے"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"رسائی پوائنٹ عارضی طور پر فُل ہے"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"‏منسلک بذریعہ ‎%1$s"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"‏دستیاب بذریعہ ‎%1$s"</string>
-    <!-- no translation found for osu_opening_provider (5488997661548640424) -->
-    <skip />
-    <!-- no translation found for osu_connect_failed (2187750899158158934) -->
-    <skip />
-    <!-- no translation found for osu_completing_sign_up (9037638564719197082) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_failed (7296159750352873260) -->
-    <skip />
-    <!-- no translation found for osu_sign_up_complete (8207626049093289203) -->
-    <skip />
+    <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> کھل رہا ہے"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"منسلک نہیں کیا جا سکا"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"سائن اپ مکمل ہو رہا ہے…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"سائن اپ مکمل نہیں ہو سکا۔ دوبارہ کوشش کرنے کے لیے تھپتھپائیں۔"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"سائن اپ مکمل ہو گیا۔ منسلک ہو رہا ہے…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"بہت سست"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"سست"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ٹھیک ہے"</string>
@@ -94,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"‏SIM رسائی"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"‏HD آڈیو: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"‏HD آڈیو"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سماعتی آلہ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"سماعتی آلے سے منسلک"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سماعتی آلات"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"سماعتی آلات سے منسلک ہے"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"میڈیا آڈیو سے مربوط"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"فون آڈیو سے مربوط"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"فائل منتقلی سرور سے مربوط ہو گیا ہے"</string>
@@ -112,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"فون آڈیو کیلئے استعمال کریں"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"فائل منتقل کرنے کیلئے استعمال کریں"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ان پٹ کیلئے استعمال"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"سماعتی آلے کیلئے استعمال کریں"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"سماعتی آلات کے لیے استعمال کریں"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"جوڑا بنائیں"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"جوڑا بنائیں"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"منسوخ کریں"</string>
@@ -143,6 +136,8 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string>
+    <!-- no translation found for data_usage_ota (5377889154805560860) -->
+    <skip />
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏USB ٹیدرنگ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیدرنگ"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index bcb4d27..2851baf 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kartaga kirish"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Eshitish apparati"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Eshitish apparatiga ulangan"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Eshitish apparatlari"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Eshitish apparatlariga ulangan"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Audio qurilmasiga ulangan"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon karnayiga ulanildi"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl almashinish serveriga ulanildi"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Dok’dan karnay sifatida foydalanish"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl almashinish uchun foydalanish"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kiritish qurilmasi sifatida foydalanish"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Eshitish apparati uchun foydalanish"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Eshitish apparatlari uchun foydalanish"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Biriktirish"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ULANISH"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Bekor qilish"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"O‘chirilgan ilovalar"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"O‘chirib tashlangan ilova va foydalanuvchilar"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Tizimni yangilash"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB modem"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ixcham hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth modem"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index d03b2f1..3fb101e 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Quyền truy cập SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Âm thanh HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Âm thanh HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Trợ thính"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Đã kết nối với thiết bị trợ thính"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Thiết bị trợ thính"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Đã kết nối với Thiết bị trợ thính"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Đã kết nối với âm thanh phương tiện"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Đã kết nối với âm thanh điện thoại"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Đã kết nối với máy chủ chuyển tệp"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sử dụng cho âm thanh điện thoại"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sử dụng để chuyển tệp"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Sử dụng để nhập"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Sử dụng cho thiết bị trợ thính"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Dùng cho Thiết bị trợ thính"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Ghép nối"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"GHÉP NỐI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hủy"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Hệ điều hành Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Ứng dụng đã xóa"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Ứng dụng và người dùng bị xóa"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Bản cập nhật hệ thống"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Chia sẻ kết nối Internet qua USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Điểm phát sóng di động"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Chia sẻ kết nối Internet qua Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 6967447..0a318d7 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取权限"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 音频:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 音频"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助听器"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已连接到助听器"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助听器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已连接到助听器"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已连接到媒体音频"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已连接到手机音频"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已连接到文件传输服务器"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用于手机音频"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用于文件传输"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用于输入"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用于助听器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用于助听器"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配对"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配对"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android 操作系统"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已删除的应用"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已删除的应用和用户"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"系统更新"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 网络共享"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"便携式热点"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"蓝牙网络共享"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 5394afc..06bee94 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -38,9 +38,9 @@
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"已透過 %1$s 自動連線"</string>
     <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"已透過網絡評分供應商自動連線"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
-    <string name="connected_via_app" msgid="5571999941988929520">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
+    <string name="connected_via_app" msgid="5571999941988929520">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string>
-    <string name="tap_to_sign_up" msgid="6449724763052579434">"輕觸即可註冊"</string>
+    <string name="tap_to_sign_up" msgid="6449724763052579434">"輕按即可登入"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,但沒有互聯網"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有互聯網連線"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string>
@@ -48,10 +48,10 @@
     <string name="connected_via_carrier" msgid="7583780074526041912">"已透過 %1$s 連線"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"可透過 %1$s 連線"</string>
     <string name="osu_opening_provider" msgid="5488997661548640424">"正在開啟 <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
-    <string name="osu_connect_failed" msgid="2187750899158158934">"無法連線"</string>
-    <string name="osu_completing_sign_up" msgid="9037638564719197082">"正在完成註冊程序…"</string>
-    <string name="osu_sign_up_failed" msgid="7296159750352873260">"無法完成註冊程序。輕觸即可重試。"</string>
-    <string name="osu_sign_up_complete" msgid="8207626049093289203">"註冊完成。連線中…"</string>
+    <string name="osu_connect_failed" msgid="2187750899158158934">"無法連接"</string>
+    <string name="osu_completing_sign_up" msgid="9037638564719197082">"正在完成申請…"</string>
+    <string name="osu_sign_up_failed" msgid="7296159750352873260">"無法完成申請。輕按即可重試。"</string>
+    <string name="osu_sign_up_complete" msgid="8207626049093289203">"已完成申請。連接中…"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"非常慢"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"慢"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"良好"</string>
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"高清音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"高清音訊"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助聽器"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已連線至助聽器"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助聽器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已連接助聽器"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已連接媒體音頻裝置"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已連接手機耳機"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線至檔案傳輸伺服器"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於手機音效"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用於助聽器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用於助聽器"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已移除的應用程式"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"系統更新"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網絡共享"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式熱點"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網絡共享"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 107eb6d..a5074d9 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取權"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 高解析音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 高解析音訊"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助聽器"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已連接到助聽器"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助聽器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已連接到助聽器"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"連接至媒體音訊"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"連接至電話音訊"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線到檔案傳輸伺服器"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於電話音訊"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用於助聽器"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用於助聽器"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"移除的應用程式"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"系統更新"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網路共用"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式無線基地台"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網路共用"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 920df4b..5892093 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -87,8 +87,8 @@
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ukufinyelela kwe-SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Umsindo we-HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Umsindo we-HD"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Usizo lokuzwa"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Ixhunywe kokokusiza ukuzwa"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Izinsiza zokuzwa"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Kuxhumeke kwizinsiza zokuzwa"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ixhume emsindweni wemidiya"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ixhunywe kumsindo wefoni"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ixhunywe kwiseva yokudlulisa ifayela"</string>
@@ -105,7 +105,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sebenziselwa umsindo wefoni"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sebenziselwa ukudlulisa ifayela"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Isetshenziselwa okufakwayo"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Sebenzisela usizo lokuzwa"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Sebenzisa izinsiza zokuzwa"</string>
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bhangqa"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BHANQA"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Khansela"</string>
@@ -136,6 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"I-Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Izinhlelo zokusebenza zisusiwe"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Izinhelo zokusebenza nabasebenzisi abasusiwe"</string>
+    <string name="data_usage_ota" msgid="5377889154805560860">"Izibuyekezo zesistimu"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Imodemu nge-USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"I-hotspot ephathekayo"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Imodemu nge-Bluetooth"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
index e92b36a..b7f7ad2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -23,6 +23,10 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
 
 /**
  * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
@@ -38,12 +42,22 @@
     public static final String EXTRA_URI = "uri";
     public static final String EXTRA_RECEIVER = "receiver";
     public static final String EXTRA_FILTER = "filter";
+    private static final String TAG = "SliceBroadcastRelay";
 
-    public static void registerReceiver(Context context, Uri registerKey,
+    private static final Set<Uri> sRegisteredUris = new ArraySet<>();
+
+    /**
+     * Associate intent filter/sliceUri with corresponding receiver.
+     */
+    public static void registerReceiver(Context context, Uri sliceUri,
             Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+
+        Log.d(TAG, "Registering Uri for broadcast relay: " + sliceUri);
+        sRegisteredUris.add(sliceUri);
+
         Intent registerBroadcast = new Intent(ACTION_REGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
         registerBroadcast.putExtra(EXTRA_RECEIVER,
                 new ComponentName(context.getPackageName(), receiver.getName()));
@@ -52,12 +66,21 @@
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
     }
 
-    public static void unregisterReceivers(Context context, Uri registerKey) {
-        Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+    /**
+     * Unregisters all receivers for a given slice uri.
+     */
+
+    public static void unregisterReceivers(Context context, Uri sliceUri) {
+        if (!sRegisteredUris.contains(sliceUri)) {
+            return;
+        }
+        Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri);
+        final Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
 
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+        sRegisteredUris.remove(sliceUri);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index a1bf936..c1933fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -73,9 +73,8 @@
                         BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
             }
-
-            mProfileManager.callServiceConnectedListeners();
             mIsProfileReady=true;
+            mProfileManager.callServiceConnectedListeners();
         }
 
         public void onServiceDisconnected(int profile) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 305a1ff..dd6d563 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -40,6 +40,7 @@
 import com.android.settingslib.NetworkPolicyEditor;
 
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
 import java.util.Iterator;
 
 /**
@@ -52,6 +53,7 @@
     protected final int mNetworkType;
     private final NetworkPolicy mPolicy;
     private final NetworkTemplate mNetworkTemplate;
+    private final ArrayList<Long> mCycles;
     @VisibleForTesting
     final INetworkStatsService mNetworkStatsService;
 
@@ -60,6 +62,7 @@
         mSubId = builder.mSubId;
         mNetworkType = builder.mNetworkType;
         mNetworkTemplate = builder.mNetworkTemplate;
+        mCycles = builder.mCycles;
         mNetworkStatsManager = (NetworkStatsManager)
             builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
         mNetworkStatsService = INetworkStatsService.Stub.asInterface(
@@ -77,7 +80,9 @@
     }
 
     public D loadInBackground() {
-        if (mPolicy == null) {
+        if (mCycles != null && mCycles.size() > 1) {
+            loadDataForSpecificCycles();
+        } else if (mPolicy == null) {
             loadFourWeeksData();
         } else {
             loadPolicyData();
@@ -132,6 +137,17 @@
     }
 
     @VisibleForTesting
+    void loadDataForSpecificCycles() {
+        long cycleEnd = mCycles.get(0);
+        final int lastCycleIndex = mCycles.size() - 1;
+        for (int i = 1; i <= lastCycleIndex; i++) {
+            final long cycleStart = mCycles.get(i);
+            recordUsage(cycleStart, cycleEnd);
+            cycleEnd = cycleStart;
+        }
+    }
+
+    @VisibleForTesting
     abstract void recordUsage(long start, long end);
 
     abstract D getCycleUsage();
@@ -157,11 +173,17 @@
         return bytes;
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    public ArrayList<Long> getCycles() {
+        return mCycles;
+    }
+
     public static abstract class Builder<T extends NetworkCycleDataLoader> {
         private final Context mContext;
         private String mSubId;
         private int mNetworkType;
         private NetworkTemplate mNetworkTemplate;
+        private ArrayList<Long> mCycles;
 
         public Builder (Context context) {
             mContext = context;
@@ -178,6 +200,16 @@
             return this;
         }
 
+        /**
+         * Sets the network cycles to be used to query the usage data.
+         * @param cycles the time slots for the network cycle to be used to query the network usage.
+         * @return the builder
+         */
+        public Builder<T> setCycles(ArrayList<Long> cycles) {
+            mCycles = cycles;
+            return this;
+        }
+
         public abstract T build();
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
index a106846..2a12810 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
@@ -56,7 +56,7 @@
     }
 
     private void init() {
-        setIcon(com.android.internal.R.drawable.ic_info_outline_24);
+        setIcon(R.drawable.ic_info_outline_24);
         setKey(KEY_FOOTER);
         setOrder(ORDER_FOOTER);
         setSelectable(false);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ac2c2c9..43affcd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1065,7 +1065,7 @@
     }
 
     public boolean isSaved() {
-        return networkId != WifiConfiguration.INVALID_NETWORK_ID;
+        return mConfig != null;
     }
 
     public Object getTag() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index b9a5f23..8ea0f4b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -53,6 +53,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.util.CollectionUtils;
 import com.android.settingslib.R;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -566,9 +567,6 @@
 
                 AccessPoint accessPoint =
                         getCachedOrCreate(entry.getValue(), cachedAccessPoints);
-                if (mLastInfo != null && mLastNetworkInfo != null) {
-                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
-                }
 
                 // Update the matching config if there is one, to populate saved network info
                 accessPoint.update(configsByKey.get(entry.getKey()));
@@ -578,68 +576,20 @@
 
             List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
 
-            // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN.
-            List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans =
-                    mWifiManager.getAllMatchingWifiConfigs(cachedScanResults);
-            Set<String> seenFQDNs = new ArraySet<>();
-            for (Pair<WifiConfiguration,
-                    Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
-                WifiConfiguration config = pairing.first;
+            // Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN.
+            accessPoints.addAll(updatePasspointAccessPoints(
+                    mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints));
 
-                List<ScanResult> scanResults = new ArrayList<>();
+            // Add OSU Provider AccessPoints
+            accessPoints.addAll(updateOsuAccessPoints(
+                    mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints));
 
-                List<ScanResult> homeScans =
-                        pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
-                List<ScanResult> roamingScans =
-                        pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
-
-                if (homeScans == null) {
-                    homeScans = new ArrayList<>();
-                }
-                if (roamingScans == null) {
-                    roamingScans = new ArrayList<>();
-                }
-
-                // TODO(b/118705403): Differentiate home network vs roaming network for summary info
-                if (!homeScans.isEmpty()) {
-                    scanResults.addAll(homeScans);
-                } else {
-                    scanResults.addAll(roamingScans);
-                }
-
-                if (seenFQDNs.add(config.FQDN)) {
-                    int bestRssi = Integer.MIN_VALUE;
-                    for (ScanResult result : scanResults) {
-                        if (result.level >= bestRssi) {
-                            bestRssi = result.level;
-                            config.SSID = AccessPoint.convertToQuotedString(result.SSID);
-                        }
-                    }
-
-                    AccessPoint accessPoint =
-                            getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config);
-                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
-                    accessPoints.add(accessPoint);
+            if (mLastInfo != null && mLastNetworkInfo != null) {
+                for (AccessPoint ap : accessPoints) {
+                    ap.update(connectionConfig, mLastInfo, mLastNetworkInfo);
                 }
             }
 
-            // Add Passpoint OSU Provider AccessPoints
-            Map<OsuProvider, List<ScanResult>> providersAndScans =
-                    mWifiManager.getMatchingOsuProviders(cachedScanResults);
-            Set<OsuProvider> alreadyProvisioned = mWifiManager
-                    .getMatchingPasspointConfigsForOsuProviders(
-                            providersAndScans.keySet()).keySet();
-            for (OsuProvider provider : providersAndScans.keySet()) {
-                if (!alreadyProvisioned.contains(provider)) {
-                    AccessPoint accessPointOsu =
-                            getCachedOrCreateOsu(providersAndScans.get(provider),
-                                    cachedAccessPoints, provider);
-                    accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo);
-                    accessPoints.add(accessPointOsu);
-                }
-            }
-
-
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
             if (accessPoints.isEmpty() && connectionConfig != null) {
@@ -686,7 +636,67 @@
     }
 
     @VisibleForTesting
-    AccessPoint getCachedOrCreate(
+    List<AccessPoint> updatePasspointAccessPoints(
+            List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans,
+            List<AccessPoint> accessPointCache) {
+        List<AccessPoint> accessPoints = new ArrayList<>();
+
+        Set<String> seenFQDNs = new ArraySet<>();
+        for (Pair<WifiConfiguration,
+                Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
+            WifiConfiguration config = pairing.first;
+            if (seenFQDNs.add(config.FQDN)) {
+                List<ScanResult> apScanResults = new ArrayList<>();
+
+                List<ScanResult> homeScans =
+                        pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+                List<ScanResult> roamingScans =
+                        pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+                // TODO(b/118705403): Differentiate home network vs roaming network for summary info
+                if (!CollectionUtils.isEmpty(homeScans)) {
+                    apScanResults.addAll(homeScans);
+                } else if (!CollectionUtils.isEmpty(roamingScans)) {
+                    apScanResults.addAll(roamingScans);
+                }
+
+                int bestRssi = Integer.MIN_VALUE;
+                for (ScanResult result : apScanResults) {
+                    if (result.level >= bestRssi) {
+                        bestRssi = result.level;
+                        config.SSID = AccessPoint.convertToQuotedString(result.SSID);
+                    }
+                }
+
+                AccessPoint accessPoint =
+                        getCachedOrCreatePasspoint(apScanResults, accessPointCache, config);
+                accessPoints.add(accessPoint);
+            }
+        }
+        return accessPoints;
+    }
+
+    @VisibleForTesting
+    List<AccessPoint> updateOsuAccessPoints(
+            Map<OsuProvider, List<ScanResult>> providersAndScans,
+            List<AccessPoint> accessPointCache) {
+        List<AccessPoint> accessPoints = new ArrayList<>();
+
+        Set<OsuProvider> alreadyProvisioned = mWifiManager
+                .getMatchingPasspointConfigsForOsuProviders(
+                        providersAndScans.keySet()).keySet();
+        for (OsuProvider provider : providersAndScans.keySet()) {
+            if (!alreadyProvisioned.contains(provider)) {
+                AccessPoint accessPointOsu =
+                        getCachedOrCreateOsu(providersAndScans.get(provider),
+                                accessPointCache, provider);
+                accessPoints.add(accessPointOsu);
+            }
+        }
+        return accessPoints;
+    }
+
+    private AccessPoint getCachedOrCreate(
             List<ScanResult> scanResults,
             List<AccessPoint> cache) {
         AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0)));
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 42eb0b9..7d22788 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -48,11 +49,15 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiSsid;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -74,7 +79,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -94,6 +102,8 @@
     private static final int RSSI_1 = -30;
     private static final byte SCORE_1 = 10;
     private static final int BADGE_1 = AccessPoint.Speed.MODERATE;
+    private static final String FQDN_1 = "fqdn1";
+    private static final String PROVIDER_FRIENDLY_NAME_1 = "providerFriendlyName1";
 
     private static final String SSID_2 = "ssid2";
     private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA";
@@ -102,6 +112,8 @@
     private static final int RSSI_2 = -30;
     private static final byte SCORE_2 = 15;
     private static final int BADGE_2 = AccessPoint.Speed.FAST;
+    private static final String FQDN_2 = "fqdn2";
+    private static final String PROVIDER_FRIENDLY_NAME_2 = "providerFriendlyName2";
 
     private static final String SSID_3 = "ssid3";
     private static final String BSSID_3 = "CC:00:00:00:00:00";
@@ -271,6 +283,61 @@
                 0 /* microsecond timestamp */);
     }
 
+    private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) {
+        WifiConfiguration config = spy(new WifiConfiguration());
+        config.FQDN = fqdn;
+        config.providerFriendlyName = friendlyName;
+        when(config.isPasspoint()).thenReturn(true);
+        return config;
+    }
+
+    private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>>
+            createPasspointMatchingWifiConfigsWithDuplicates() {
+        List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList =
+                new ArrayList<>();
+        Map<Integer, List<ScanResult>> mapping = new HashMap<>();
+
+        mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, Arrays.asList(buildScanResult1()));
+
+        WifiConfiguration passpointConfig1 =
+                buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1);
+        WifiConfiguration passpointConfig2 =
+                buildPasspointConfiguration(FQDN_2, PROVIDER_FRIENDLY_NAME_2);
+
+        matchingList.add(new Pair(passpointConfig1, mapping));
+        matchingList.add(new Pair(passpointConfig1, mapping));
+        matchingList.add(new Pair(passpointConfig2, mapping));
+        matchingList.add(new Pair(passpointConfig2, mapping));
+
+        return matchingList;
+    }
+
+    private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>>
+            createPasspointMatchingWifiConfigWithScanResults(
+            List<ScanResult> homeList, List<ScanResult> roamingList) {
+        List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList =
+                new ArrayList<>();
+        Map<Integer, List<ScanResult>> mapping = new HashMap<>();
+
+        if (homeList != null) {
+            mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, homeList);
+        }
+        if (roamingList != null) {
+            mapping.put(WifiManager.PASSPOINT_ROAMING_NETWORK, roamingList);
+        }
+
+        matchingList.add(new Pair(buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1),
+                mapping));
+
+        return matchingList;
+    }
+
+    private static OsuProvider buildOsuProvider(String friendlyName) {
+        Map<String, String> friendlyNames = new HashMap<>();
+        friendlyNames.put("en", friendlyName);
+        return new OsuProvider(null, friendlyNames, null, null, null, null, null);
+    }
+
     private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
                     Intent ... intents)
             throws InterruptedException {
@@ -926,4 +993,172 @@
         assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
         assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
     }
+
+    /**
+     * Verifies that updatePasspointAccessPoints will only return AccessPoints whose
+     * isPasspoint() evaluates as true.
+     */
+    @Test
+    public void updatePasspointAccessPoints_returnedAccessPointsArePasspoint() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints(
+                createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>());
+
+        assertTrue(passpointAccessPoints.size() != 0);
+        for (AccessPoint ap : passpointAccessPoints) {
+            assertTrue(ap.isPasspoint());
+        }
+    }
+
+    /**
+     * Verifies that updatePasspointAccessPoints will return the same amount of AccessPoints as
+     * unique WifiConfigurations, even if duplicate FQDNs exist.
+     */
+    @Test
+    public void updatePasspointAccessPoints_ignoresDuplicateFQDNs() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        // Process matching list of four configs with two duplicate FQDNs.
+        List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints(
+                createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>());
+
+        // Should have 2 APs with unique FQDNs, ignoring the 2 duplicate FQDNs.
+        assertThat(passpointAccessPoints).hasSize(2);
+
+        Set<String> fqdns = new ArraySet<>(Arrays.asList(FQDN_1, FQDN_2));
+
+        assertTrue(fqdns.remove(passpointAccessPoints.get(0).getConfig().FQDN));
+        assertTrue(fqdns.remove(passpointAccessPoints.get(1).getConfig().FQDN));
+    }
+
+    /**
+     * Verifies that updatePasspointAccessPoints will return matching cached APs and update their
+     * scan results instead of creating new APs.
+     */
+    @Test
+    public void updatePasspointAccessPoints_usesCachedAccessPoints() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        ScanResult result = buildScanResult1();
+
+        List<AccessPoint> passpointAccessPointsFirstUpdate = tracker.updatePasspointAccessPoints(
+                createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result),
+                        null), new ArrayList<>());
+        List<AccessPoint> cachedAccessPoints = new ArrayList<>(passpointAccessPointsFirstUpdate);
+
+        int prevRssi = result.level;
+        int newRssi = prevRssi + 10;
+        result.level = newRssi;
+
+        List<AccessPoint> passpointAccessPointsSecondUpdate = tracker.updatePasspointAccessPoints(
+                createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result),
+                        null), cachedAccessPoints);
+
+        // Verify second update AP is the same object as the first update AP
+        assertThat(passpointAccessPointsFirstUpdate.get(0))
+                .isSameAs(passpointAccessPointsSecondUpdate.get(0));
+        // Verify second update AP has the average of the first and second update RSSIs
+        assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi())
+                .isEqualTo((prevRssi + newRssi) / 2);
+    }
+
+    /**
+     * Verifies that updateOsuAccessPoints will only return AccessPoints whose
+     * isOsuProvider() evaluates as true.
+     */
+    @Test
+    public void updateOsuAccessPoints_returnedAccessPointsAreOsuProviders() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>();
+        providersAndScans.put(
+                buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1()));
+        providersAndScans.put(
+                buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2()));
+
+        List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints(
+                providersAndScans, new ArrayList<>());
+
+        assertThat(osuAccessPoints).hasSize(2);
+        for (AccessPoint ap: osuAccessPoints) {
+            assertThat(ap.isOsuProvider()).isTrue();
+        }
+    }
+
+    /**
+     * Verifies that updateOsuAccessPoints will not return Osu AccessPoints for already provisioned
+     * networks
+     */
+    @Test
+    public void updateOsuAccessPoints_doesNotReturnAlreadyProvisionedOsuAccessPoints() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        // Start with two Osu Providers
+        Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>();
+        providersAndScans.put(
+                buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1()));
+        providersAndScans.put(
+                buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2()));
+
+        // First update
+        List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints(
+                providersAndScans, new ArrayList<>());
+
+        // Make sure both Osu Providers' APs are returned
+        assertThat(osuAccessPoints).hasSize(2);
+        List<String> friendlyNames = Arrays.asList(
+                osuAccessPoints.get(0).getTitle(), osuAccessPoints.get(1).getTitle());
+        assertThat(friendlyNames)
+                .containsExactly(PROVIDER_FRIENDLY_NAME_1, PROVIDER_FRIENDLY_NAME_2);
+
+        // Simulate Osu Provider 1 being provisioned
+        Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigForOsuProvider =
+                new HashMap<>();
+        matchingPasspointConfigForOsuProvider.put(buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), null);
+        when(mockWifiManager.getMatchingPasspointConfigsForOsuProviders(any())).thenReturn(
+                matchingPasspointConfigForOsuProvider);
+
+        // Second update
+        osuAccessPoints = tracker.updateOsuAccessPoints(
+                providersAndScans, new ArrayList<>());
+
+        // Returned AP should only be for Osu Provider 2
+        assertThat(osuAccessPoints).hasSize(1);
+        assertThat(osuAccessPoints.get(0).getTitle()).isEqualTo(PROVIDER_FRIENDLY_NAME_2);
+    }
+
+    /**
+     * Verifies that updateOsuAccessPoints will return matching cached APs and update their
+     * scan results instead of creating new APs.
+     */
+    @Test
+    public void updateOsuAccessPoints_usesCachedAccessPoints() {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        ScanResult result = buildScanResult1();
+
+        Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>();
+        providersAndScans.put(
+                buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(result));
+
+        List<AccessPoint> osuAccessPointsFirstUpdate = tracker.updateOsuAccessPoints(
+                providersAndScans, new ArrayList<>());
+        List<AccessPoint> cachedAccessPoints = new ArrayList<>(osuAccessPointsFirstUpdate);
+
+        // New RSSI for second update
+        int prevRssi = result.level;
+        int newRssi = prevRssi + 10;
+        result.level = newRssi;
+
+        List<AccessPoint> osuAccessPointsSecondUpdate = tracker.updateOsuAccessPoints(
+                providersAndScans, cachedAccessPoints);
+
+        // Verify second update AP is the same object as the first update AP
+        assertTrue(osuAccessPointsFirstUpdate.get(0)
+                == osuAccessPointsSecondUpdate.get(0));
+        // Verify second update AP has the average of the first and second update RSSIs
+        assertThat(osuAccessPointsSecondUpdate.get(0).getRssi())
+                .isEqualTo((prevRssi + newRssi) / 2);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index b8a143a..c5f54bb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -46,6 +46,7 @@
 import org.robolectric.util.ReflectionHelpers;
 
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -99,6 +100,20 @@
     }
 
     @Test
+    public void loadInBackground_hasCyclePeriod_shouldLoadDataForSpecificCycles() {
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        doNothing().when(mLoader).loadDataForSpecificCycles();
+        final ArrayList<Long> cycles = new ArrayList<>();
+        cycles.add(67890L);
+        cycles.add(12345L);
+        ReflectionHelpers.setField(mLoader, "mCycles", cycles);
+
+        mLoader.loadInBackground();
+
+        verify(mLoader).loadDataForSpecificCycles();
+    }
+
+    @Test
     public void loadPolicyData_shouldRecordUsageFromPolicyCycle() {
         final int networkType = ConnectivityManager.TYPE_MOBILE;
         final String subId = "TestSubscriber";
@@ -139,6 +154,27 @@
         verify(mLoader).recordUsage(fourWeeksAgo, now);
     }
 
+    @Test
+    public void loadDataForSpecificCycles_shouldRecordUsageForSpecifiedTime() {
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        final long now = System.currentTimeMillis();
+        final long tenDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 10);
+        final long twentyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 20);
+        final long thirtyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 30);
+        final ArrayList<Long> cycles = new ArrayList<>();
+        cycles.add(now);
+        cycles.add(tenDaysAgo);
+        cycles.add(twentyDaysAgo);
+        cycles.add(thirtyDaysAgo);
+        ReflectionHelpers.setField(mLoader, "mCycles", cycles);
+
+        mLoader.loadDataForSpecificCycles();
+
+        verify(mLoader).recordUsage(tenDaysAgo, now);
+        verify(mLoader).recordUsage(twentyDaysAgo, tenDaysAgo);
+        verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo);
+    }
+
     public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
 
         private NetworkCycleDataTestLoader(Context context) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index c3ea336..1080cf4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.TextView;
 
@@ -46,6 +47,7 @@
     private BarView mBarView2;
     private BarView mBarView3;
     private BarView mBarView4;
+    private TextView mTitleView;
     private TextView mDetailsView;
     private PreferenceViewHolder mHolder;
     private BarChartPreference mPreference;
@@ -63,6 +65,7 @@
         mBarView2 = mBarChartView.findViewById(R.id.bar_view2);
         mBarView3 = mBarChartView.findViewById(R.id.bar_view3);
         mBarView4 = mBarChartView.findViewById(R.id.bar_view4);
+        mTitleView = mBarChartView.findViewById(R.id.bar_chart_title);
         mDetailsView = mBarChartView.findViewById(R.id.bar_chart_details);
 
         mBarChartInfo = new BarChartInfo.Builder()
@@ -76,7 +79,6 @@
 
     @Test
     public void initializeBarChart_titleSet_shouldSetTitleInChartView() {
-        final TextView titleView = mBarChartView.findViewById(R.id.bar_chart_title);
         final BarChartInfo barChartInfo = new BarChartInfo.Builder()
                 .setTitle(R.string.debug_app)
                 .build();
@@ -84,8 +86,8 @@
         mPreference.initializeBarChart(barChartInfo);
         mPreference.onBindViewHolder(mHolder);
 
-        assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+        assertThat(mTitleView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mTitleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
     }
 
     @Test
@@ -99,8 +101,7 @@
         // We don't add any bar view yet.
         mPreference.onBindViewHolder(mHolder);
 
-        assertThat(mBarChartView.findViewById(R.id.bar_chart_title).getVisibility())
-                .isEqualTo(View.VISIBLE);
+        assertThat(mTitleView.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mBarChartView.findViewById(R.id.empty_view).getVisibility())
                 .isEqualTo(View.VISIBLE);
         assertThat(mBarChartView.findViewById(R.id.bar_views_container).getVisibility())
@@ -302,4 +303,38 @@
         assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mBarView1.hasOnClickListeners()).isTrue();
     }
+
+    @Test
+    public void onBindViewHolder_loadingStateIsTrue_shouldNotInitAnyView() {
+        final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+        viewInfo.setClickListener(v -> {
+        });
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.updateLoadingState(true /* isLoading */);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(TextUtils.isEmpty(mTitleView.getText())).isTrue();
+        assertThat(TextUtils.isEmpty(mDetailsView.getText())).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_loadingStateIsFalse_shouldInitAnyView() {
+        final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+        viewInfo.setClickListener(v -> {
+        });
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.updateLoadingState(false /* isLoading */);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(TextUtils.isEmpty(mTitleView.getText())).isFalse();
+        assertThat(TextUtils.isEmpty(mDetailsView.getText())).isFalse();
+    }
 }
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
new file mode 100644
index 0000000..1c97fc3
--- /dev/null
+++ b/packages/SettingsProvider/Android.bp
@@ -0,0 +1,40 @@
+android_app {
+    name: "SettingsProvider",
+    resource_dirs: ["res"],
+    srcs: [
+        "src/**/*.java",
+        "src/com/android/providers/settings/EventLogTags.logtags",
+    ],
+    libs: [
+        "telephony-common",
+        "ims-common",
+    ],
+    static_libs: ["junit"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
+
+android_test {
+    name: "SettingsProviderTest",
+    // Note we statically link several classes to do some unit tests.  It's not accessible otherwise
+    // because this test is not an instrumentation test. (because the target runs in the system process.)
+    srcs: [
+        "test/**/*.java",
+        "src/com/android/providers/settings/SettingsState.java",
+        "src/com/android/providers/settings/SettingsHelper.java",
+    ],
+    static_libs: ["androidx.test.rules"],
+    libs: ["android.test.base"],
+    resource_dirs: ["res"],
+    aaptflags: [
+        "--auto-add-overlay",
+        "--extra-packages",
+        "com.android.providers.settings",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    manifest: "test/AndroidManifest.xml",
+    test_config: "test/AndroidTest.xml",
+}
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
deleted file mode 100644
index ccde571..0000000
--- a/packages/SettingsProvider/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    src/com/android/providers/settings/EventLogTags.logtags
-
-LOCAL_JAVA_LIBRARIES := telephony-common ims-common
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-LOCAL_PACKAGE_NAME := SettingsProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6152b8c..81b304d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -698,6 +698,9 @@
                 Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST,
                 GlobalSettingsProto.Gpu.ANGLE_WHITELIST);
         dumpSetting(s, p,
+                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
+                GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG);
+        dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYER_APP,
                 GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
         dumpSetting(s, p,
@@ -718,6 +721,12 @@
         dumpSetting(s, p,
                 Settings.Global.GAME_DRIVER_WHITELIST,
                 GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_BLACKLISTS,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLISTS);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_SPHAL_LIBRARIES);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
deleted file mode 100644
index ac97adb..0000000
--- a/packages/SettingsProvider/test/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# Note we statically link several classes to do some unit tests.  It's not accessible otherwise
-# because this test is not an instrumentation test. (because the target runs in the system process.)
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-    ../src/com/android/providers/settings/SettingsState.java \
-    ../src/com/android/providers/settings/SettingsHelper.java
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_JAVA_LIBRARIES := android.test.base
-
-LOCAL_RESOURCE_DIR := frameworks/base/packages/SettingsProvider/res
-
-LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.providers.settings
-
-LOCAL_PACKAGE_NAME := SettingsProviderTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp
new file mode 100644
index 0000000..5380832
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2011 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.
+//
+
+android_app {
+    name: "SharedStorageBackup",
+    srcs: ["src/**/*.java"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
deleted file mode 100644
index 2e07ab1..0000000
--- a/packages/SharedStorageBackup/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# Copyright (C) 2011 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_PACKAGE_NAME := SharedStorageBackup
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
new file mode 100644
index 0000000..586292e
--- /dev/null
+++ b/packages/StatementService/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+android_app {
+    name: "StatementService",
+    srcs: ["src/**/*.java"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+    platform_apis: true,
+    privileged: true,
+    libs: ["org.apache.http.legacy"],
+    static_libs: [
+        "libprotobuf-java-nano",
+        "volley",
+    ],
+}
diff --git a/packages/StatementService/Android.mk b/packages/StatementService/Android.mk
deleted file mode 100644
index b9b29e7..0000000
--- a/packages/StatementService/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_PACKAGE_NAME := StatementService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    libprotobuf-java-nano \
-    volley
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH)/src)
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e0d178f..29ae1b2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -137,6 +137,7 @@
     static_libs: [
         "SystemUI-core",
     ],
+    resource_dirs: [],
 
     platform_apis: true,
     product_specific: true,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3778a9c..265d464 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -22,6 +22,11 @@
         android:sharedUserId="android.uid.systemui"
         coreApp="true">
 
+    <!-- Using OpenGL ES 2.0 -->
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true" />
+
     <!-- SysUI must be the one to define this permission; its name is
          referenced by the core OS. -->
     <permission android:name="android.permission.systemui.IDENTITY"
@@ -126,6 +131,7 @@
     <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
+    <uses-permission android:name="android.permission.MANAGE_BIOMETRIC" />
     <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
 
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
deleted file mode 100644
index 67f072f..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
deleted file mode 100644
index 63927bc..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
deleted file mode 100644
index a538149..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 51f6a4b..dc45b4b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,11 +28,20 @@
     android:clipToPadding="false"
     android:orientation="vertical"
     android:layout_centerHorizontal="true">
+    <TextView
+              android:id="@+id/title"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:paddingStart="64dp"
+              android:paddingEnd="64dp"
+              android:visibility="gone"
+              android:textColor="?attr/wallpaperTextColor"
+              android:theme="@style/TextAppearance.Keyguard"
+    />
     <view class="com.android.keyguard.KeyguardSliceView$Row"
               android:id="@+id/row"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginTop="@dimen/subtitle_clock_padding"
               android:orientation="horizontal"
               android:gravity="center"
     />
diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml
index b61ad9c..9f7ea0d 100644
--- a/packages/SystemUI/res-keyguard/layout/text_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml
@@ -16,9 +16,10 @@
   -->
 <TextClock
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal"
+    android:gravity="center_horizontal"
     android:letterSpacing="0.03"
     android:textColor="?attr/wallpaperTextColor"
     android:singleLine="true"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index e2ba23e..b6a41c1 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,18 +42,18 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Slice header -->
-    <dimen name="widget_title_font_size">24dp</dimen>
-    <dimen name="widget_title_bottom_margin">14dp</dimen>
-    <dimen name="bottom_text_spacing_digital">0dp</dimen>
+    <dimen name="widget_title_font_size">22dp</dimen>
+    <dimen name="header_subtitle_padding">4dp</dimen>
+    <dimen name="header_icon_size">20dp</dimen>
     <!-- Slice subtitle -->
     <dimen name="widget_label_font_size">16dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
+    <dimen name="bottom_text_spacing_digital">0dp</dimen>
     <!-- Clock with header -->
-    <dimen name="widget_small_clock_padding">-25dp</dimen>
-    <dimen name="widget_small_font_size">24dp</dimen>
-    <dimen name="widget_small_font_stroke">0.6dp</dimen>
+    <dimen name="widget_small_font_size">22dp</dimen>
     <dimen name="widget_vertical_padding">32dp</dimen>
+    <dimen name="widget_vertical_padding_clock">30dp</dimen>
     <!-- Subtitle paddings -->
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index b673e4f..dd124b7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -14,12 +14,19 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal" >
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorControlNormal">
+
     <path
-        android:pathData="M13.51,12l3.75,-3.74c0.41,-0.41 0.41,-1.07 0,-1.48l-4.47,-4.47 -0.03,-0.03a1.046,1.046 0,0 0,-1.76 0.76v6.44L6.95,5.43c-0.41,-0.41 -1.06,-0.41 -1.47,0s-0.41,1.06 0,1.47l5.09,5.1 -5.09,5.09c-0.41,0.41 -0.41,1.06 0,1.47s1.06,0.41 1.47,0L11,14.51v6.45a1.04,1.04 0,0 0,1.75 0.76l0.05,-0.05 4.46,-4.46c0.41,-0.41 0.41,-1.07 0,-1.48L13.51,12zM12.99,9.67v-4.3l2.15,2.15 -2.15,2.15zM12.99,18.62v-4.3l2.15,2.15 -2.15,2.15zM6.06,13.06c-0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0c0.59,0.59 0.59,1.53 0,2.12zM20.06,10.94c0.59,0.59 0.59,1.54 0,2.12 -0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0z"
-        android:fillColor="#FFFFFFFF" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 5 10.5 C 5.82842712475 10.5 6.5 11.1715728753 6.5 12 C 6.5 12.8284271247 5.82842712475 13.5 5 13.5 C 4.17157287525 13.5 3.5 12.8284271247 3.5 12 C 3.5 11.1715728753 4.17157287525 10.5 5 10.5 Z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 19 10.5 C 19.8284271247 10.5 20.5 11.1715728753 20.5 12 C 20.5 12.8284271247 19.8284271247 13.5 19 13.5 C 18.1715728753 13.5 17.5 12.8284271247 17.5 12 C 17.5 11.1715728753 18.1715728753 10.5 19 10.5 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 8cc6caa..220c63c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -14,12 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="64dp"
-    android:height="64dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorControlNormal">
+
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/>
+        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
index 0a4d752..f708ed9 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2017 The Android Open Source Project
+     Copyright (C) 2019 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.
@@ -15,16 +15,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
-    android:viewportWidth="20.5"
-    android:viewportHeight="20.5">
-    <group
-        android:translateX="1.75"
-        android:translateY="1.4">
-        <path
-            android:pathData="M16.01,9.87l-6.24,-3.9v-4.7C9.77,0.57 9.21,0 8.5,0S7.23,0.57 7.23,1.28v4.7L0.99,9.88c-0.37,0.23 -0.6,0.64 -0.6,1.08v0.41c0,0.29 0.29,0.5 0.55,0.41l6.27,-1.97v4.7l-1.37,1.02c-0.21,0.16 -0.34,0.41 -0.34,0.68v0.57c0,0.15 0.12,0.23 0.27,0.2 1.67,-0.47 1.12,-0.31 2.73,-0.78 1.03,0.3 1.7,0.49 2.72,0.78 0.15,0.03 0.27,-0.06 0.27,-0.2v-0.57c0,-0.27 -0.13,-0.52 -0.34,-0.68l-1.37,-1.02v-4.7l6.27,1.97c0.28,0.09 0.55,-0.12 0.55,-0.41v-0.41c0.01,-0.45 -0.23,-0.87 -0.59,-1.09z"
-            android:fillColor="#FFF"/>
-    </group>
-</vector>
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
 
+<path
+    android:fillColor="#FFFFFF"
+    android:pathData="M21,16v-2l-8-5V3.5C13,2.67,12.33,2,11.5,2S10,2.67,10,3.5V9l-8,5v2l8-2.5V19l-2,1.5V22l3.5-1l3.5,1v-1.5L13,19v-5.5L21,16z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index f1158ef..b7b21fa 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,8 +16,8 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#4a4a4a" />
+    <solid android:color="#242424" /> <!-- 14% of white -->
     <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
-        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/>
+        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_bg.xml b/packages/SystemUI/res/drawable/rounded_bg.xml
index c23a87f..3de67de 100644
--- a/packages/SystemUI/res/drawable/rounded_bg.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg.xml
@@ -3,8 +3,8 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimary" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
-        android:topLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
         android:bottomRightRadius="0dp"
         android:topRightRadius="0dp"
         />
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
index b3bea63..7db59e9 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
@@ -3,7 +3,7 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimaryDark" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
         android:bottomRightRadius="0dp"
         android:topRightRadius="0dp"
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
index 622226f..382ca20 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
@@ -3,9 +3,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/panelColorBackground" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/corner_size"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="0dp"
         />
 </shape>
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml
index 03f18bb..e0d3f63 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml
@@ -3,9 +3,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorBackgroundFloating" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
-        android:topLeftRadius="@dimen/corner_size"
-        android:bottomRightRadius="@dimen/corner_size"
-        android:topRightRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
         />
 </shape>
diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
index a4b3c99..a62657d 100644
--- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
@@ -17,9 +17,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimaryDark" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/corner_size"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="0dp"
         />
 </shape>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml
new file mode 100644
index 0000000..480f523
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="top|right"
+    android:clipChildren="false"
+>
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:gravity="top|right"
+        android:padding="0dp"
+        android:orientation="vertical"
+        android:layoutDirection="ltr"
+        android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layoutDirection="ltr"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        >
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+        </com.android.systemui.globalactions.ListGridLayout>
+
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:layoutDirection="ltr"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:gravity="center"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
new file mode 100644
index 0000000..4f86826
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="top|left"
+    android:clipChildren="false"
+>
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:gravity="bottom|left"
+        android:padding="0dp"
+        android:orientation="vertical"
+        android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_gravity="top|left"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:layoutDirection="rtl"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:gravity="center"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_gravity="bottom|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        >
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+        </com.android.systemui.globalactions.ListGridLayout>
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml
index e6f2376..729e96e 100644
--- a/packages/SystemUI/res/layout/global_actions_grid.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid.xml
@@ -12,69 +12,61 @@
 >
 
     <LinearLayout
-        android:layout_height="290dp"
-        android:layout_width="412dp"
-        android:gravity="bottom"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:gravity="bottom | right"
         android:padding="0dp"
+        android:layoutDirection="ltr"
         android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
     >
         <!-- For separated items-->
         <LinearLayout
             android:id="@+id/separated_button"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
             android:layout_marginRight="@dimen/global_actions_grid_side_margin"
-            android:paddingTop="@dimen/global_actions_grid_top_padding"
-            android:paddingLeft="@dimen/global_actions_grid_left_padding"
-            android:paddingBottom="@dimen/global_actions_grid_bottom_padding"
-            android:paddingRight="@dimen/global_actions_grid_right_padding"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
             android:orientation="vertical"
             android:background="?android:attr/colorBackgroundFloating"
+            android:gravity="center"
             android:translationZ="@dimen/global_actions_translate"
         />
 
-        <Space android:layout_width="match_parent" android:layout_height="2dp"
-               android:layout_weight="1" />
-
         <!-- Grid of action items -->
         <com.android.systemui.globalactions.ListGridLayout
             android:id="@android:id/list"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:gravity="right"
             android:orientation="horizontal"
             android:layoutDirection="rtl"
             android:layout_marginRight="@dimen/global_actions_grid_side_margin"
             android:translationZ="@dimen/global_actions_translate"
-            android:paddingLeft="@dimen/global_actions_grid_left_padding"
-            android:paddingRight="@dimen/global_actions_grid_right_padding"
-            android:paddingTop="@dimen/global_actions_grid_top_padding"
-            android:paddingBottom="@dimen/global_actions_grid_bottom_padding"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
             android:background="?android:attr/colorBackgroundFloating"
         >
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
         </com.android.systemui.globalactions.ListGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 0c11cd9..5dee09d 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -18,42 +18,41 @@
      work around this for now with LinearLayouts. -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="72dp"
-    android:layout_height="72dp"
-    android:gravity="center"
+    android:layout_width="@dimen/global_actions_grid_item_width"
+    android:layout_height="@dimen/global_actions_grid_item_height"
+    android:gravity="top|center_horizontal"
     android:orientation="vertical"
     android:layout_marginTop="@dimen/global_actions_grid_item_vertical_margin"
     android:layout_marginBottom="@dimen/global_actions_grid_item_vertical_margin"
     android:layout_marginLeft="@dimen/global_actions_grid_item_side_margin"
     android:layout_marginRight="@dimen/global_actions_grid_item_side_margin"
-    android:paddingEnd="4dip"
-    android:paddingStart="4dip">
-
+>
     <ImageView
         android:id="@*android:id/icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_gravity="center"
+        android:layout_width="@dimen/global_actions_grid_item_icon_width"
+        android:layout_height="@dimen/global_actions_grid_item_icon_height"
+        android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
+        android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
+        android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
+        android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
         android:scaleType="center"
         android:alpha="?android:attr/primaryContentAlpha"
     />
 
     <TextView
         android:id="@*android:id/message"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:paddingTop="10dp"
         android:gravity="center"
-        android:textSize="12sp"
+        android:textSize="12dp"
         android:textAppearance="?android:attr/textAppearanceSmall"
     />
 
     <TextView
+        android:visibility="gone"
         android:id="@*android:id/status"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
         android:gravity="center"
         android:textColor="?android:attr/textColorTertiary"
         android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/packages/SystemUI/res/layout/home_handle.xml b/packages/SystemUI/res/layout/home_handle.xml
new file mode 100644
index 0000000..48ea5c4
--- /dev/null
+++ b/packages/SystemUI/res/layout/home_handle.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.phone.NavigationHandle
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/home_handle"
+    android:layout_width="@dimen/navigation_handle_width"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    />
+
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 58fe811..f64a64e6 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -15,6 +15,7 @@
      limitations under the License.
 -->
 
+
 <com.android.systemui.privacy.OngoingPrivacyChip
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/privacy_chip"
@@ -22,47 +23,39 @@
     android:layout_width="wrap_content"
     android:layout_marginLeft="@dimen/ongoing_appops_chip_margin"
     android:layout_marginRight="@dimen/ongoing_appops_chip_margin"
-    android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin"
-    android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin"
-    android:gravity="center_vertical|center_horizontal"
     android:layout_gravity="center_vertical|start"
+    android:gravity="center_vertical"
     android:orientation="horizontal"
-    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
-    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
     android:focusable="true">
 
-        <TextView
-            android:id="@+id/in_use_text"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|start"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:text="@string/ongoing_privacy_chip_in_use"
-            />
-
         <LinearLayout
-            android:id="@+id/icons_container"
-            android:layout_height="match_parent"
+            android:id="@+id/background"
+            android:layout_height="@dimen/ongoing_appops_chip_height"
             android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:gravity="center_vertical"
-            />
+        >
+                <LinearLayout
+                    android:id="@+id/icons_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_marginStart="@dimen/ongoing_appops_chip_items_margin"
+                    android:layout_gravity="center_vertical"
+                    android:gravity="center_vertical"
+                    />
 
-        <TextView
-            android:id="@+id/text_container"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:lines="1"
-            android:layout_gravity="center_vertical|end"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-        />
+                <TextView
+                    android:id="@+id/text_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_gravity="center_vertical|end"
+                    android:paddingStart="@dimen/ongoing_appops_chip_text_padding"
+                    android:paddingEnd="@dimen/ongoing_appops_chip_text_padding"
+                    android:gravity="center_vertical"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:lines="1"
+                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                    android:textSize="@dimen/ongoing_appops_chip_text_size"
+                    android:textColor="@color/status_bar_clock_color"
+                />
+          </LinearLayout>
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
new file mode 100644
index 0000000..586cdf3
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -0,0 +1,27 @@
+precision mediump float;
+
+uniform sampler2D uTexture;
+uniform float uCenterReveal;
+uniform float uReveal;
+uniform float uAod2Opacity;
+varying vec2 vTextureCoordinates;
+
+vec3 luminosity(vec3 color) {
+    float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+    return vec3(lum);
+}
+
+vec4 transform(vec3 diffuse) {
+    // TODO: Add well comments here, tracking on b/123615467.
+    vec3 lum = luminosity(diffuse);
+    diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal));
+    float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal));
+    diffuse = smoothstep(val, 1.0, diffuse);
+    diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal));
+    return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
+}
+
+void main() {
+    vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
+    gl_FragColor = transform(fragColor.rgb);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
new file mode 100644
index 0000000..4393e2b
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
@@ -0,0 +1,8 @@
+attribute vec4 aPosition;
+attribute vec2 aTextureCoordinates;
+varying vec2 vTextureCoordinates;
+
+void main() {
+    vTextureCoordinates = aTextureCoordinates;
+    gl_Position = aPosition;
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index df858f0..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,5 +34,4 @@
     <bool name="quick_settings_wide">true</bool>
     <dimen name="qs_detail_margin_top">0dp</dimen>
     <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
-    <dimen name="ongoing_appops_top_chip_margin">2dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw320dp-land/dimens.xml b/packages/SystemUI/res/values-sw320dp-land/dimens.xml
new file mode 100644
index 0000000..2ec5abd
--- /dev/null
+++ b/packages/SystemUI/res/values-sw320dp-land/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+
+    <!-- Global actions grid -->
+    <dimen name="global_actions_grid_vertical_padding">3dp</dimen>
+    <dimen name="global_actions_grid_horizontal_padding">0dp</dimen>
+
+    <dimen name="global_actions_grid_item_side_margin">4dp</dimen>
+    <dimen name="global_actions_grid_item_vertical_margin">5dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
new file mode 100644
index 0000000..0c2b1cc
--- /dev/null
+++ b/packages/SystemUI/res/values-sw320dp/dimens.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+
+    <!-- Global actions grid -->
+    <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen>
+
+    <dimen name="global_actions_grid_vertical_padding">0dp</dimen>
+    <dimen name="global_actions_grid_horizontal_padding">3dp</dimen>
+
+    <dimen name="global_actions_grid_item_side_margin">5dp</dimen>
+    <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen>
+    <dimen name="global_actions_grid_item_width">64dp</dimen>
+    <dimen name="global_actions_grid_item_height">64dp</dimen>
+
+    <dimen name="global_actions_grid_item_icon_width">18dp</dimen>
+    <dimen name="global_actions_grid_item_icon_height">18dp</dimen>
+    <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen>
+    <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
+    <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
new file mode 100644
index 0000000..61ba2d0
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+
+    <!-- Global actions grid -->
+    <dimen name="global_actions_grid_vertical_padding">4dp</dimen>
+    <dimen name="global_actions_grid_horizontal_padding">8dp</dimen>
+
+    <dimen name="global_actions_grid_item_side_margin">8dp</dimen>
+    <dimen name="global_actions_grid_item_vertical_margin">12dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index 5ce6524..4197eb2 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -21,4 +21,22 @@
      for different hardware and product builds. -->
 <resources>
     <dimen name="qs_detail_items_padding_top">16dp</dimen>
+
+    <!-- Global actions grid -->
+    <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen>
+
+    <dimen name="global_actions_grid_vertical_padding">8dp</dimen>
+    <dimen name="global_actions_grid_horizontal_padding">4dp</dimen>
+
+    <dimen name="global_actions_grid_item_side_margin">12dp</dimen>
+    <dimen name="global_actions_grid_item_vertical_margin">8dp</dimen>
+    <dimen name="global_actions_grid_item_width">72dp</dimen>
+    <dimen name="global_actions_grid_item_height">72dp</dimen>
+
+    <dimen name="global_actions_grid_item_icon_width">24dp</dimen>
+    <dimen name="global_actions_grid_item_icon_height">24dp</dimen>
+    <dimen name="global_actions_grid_item_icon_top_margin">16dp</dimen>
+    <dimen name="global_actions_grid_item_icon_side_margin">24dp</dimen>
+    <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 76eb85e..4396a42 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -325,6 +325,7 @@
     <!-- Nav bar button default ordering/layout -->
     <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>
     <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string>
+    <string name="config_navBarLayoutHandle" translatable="false">";home_handle;"</string>
 
     <bool name="quick_settings_show_full_alarm">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1e1245f..371a060 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -33,6 +33,11 @@
     <!-- size of the dead zone when touches have recently occurred elsewhere on screen -->
     <dimen name="navigation_bar_deadzone_size_max">32dp</dimen>
 
+    <!-- dimensions for the navigation bar handle -->
+    <dimen name="navigation_handle_width">180dp</dimen>
+    <dimen name="navigation_handle_radius">2dp</dimen>
+    <dimen name="navigation_handle_bottom">8dp</dimen>
+
     <!-- Height of notification icons in the status bar -->
     <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
 
@@ -841,26 +846,15 @@
     <dimen name="default_gear_space">18dp</dimen>
     <dimen name="cell_overlay_padding">18dp</dimen>
 
+    <!-- Global actions power menu -->
     <dimen name="global_actions_panel_width">120dp</dimen>
-
-    <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen>
-
-    <dimen name="global_actions_grid_side_margin">4dp</dimen>
-    <dimen name="global_actions_grid_separated_panel_width">104dp</dimen>
-    <dimen name="global_actions_grid_top_padding">8dp</dimen>
-    <dimen name="global_actions_grid_bottom_padding">8dp</dimen>
-    <dimen name="global_actions_grid_left_padding">4dp</dimen>
-    <dimen name="global_actions_grid_right_padding">4dp</dimen>
-
-    <dimen name="global_actions_grid_item_side_margin">12dp</dimen>
-    <dimen name="global_actions_grid_item_vertical_margin">8dp</dimen>
-
     <dimen name="global_actions_top_padding">120dp</dimen>
-
     <dimen name="global_actions_padding">12dp</dimen>
-
     <dimen name="global_actions_translate">9dp</dimen>
 
+    <!-- Global actions grid layout -->
+    <dimen name="global_actions_grid_side_margin">4dp</dimen>
+
     <!-- The maximum offset in either direction that elements are moved horizontally to prevent
          burn-in on AOD. -->
     <dimen name="burn_in_prevention_offset_x">8dp</dimen>
@@ -980,26 +974,32 @@
     <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
     <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
-    <!-- Side margins around the Ongoing App Ops chip-->
-    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
-    <!-- Top and bottom margins around the Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_top_chip_margin">12dp</dimen>
-    <!-- Start and End padding for Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
-    <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
-    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QS-->
-    <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen>
-    <!-- Icon size of Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
-    <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
     <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
     <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
+    <!-- Side margins around the Ongoing App Ops chip-->
+    <dimen name="ongoing_appops_chip_margin">0dp</dimen>
+    <!-- Height of the Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_height">32dp</dimen>
+    <!-- Start and End padding for Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_padding">8dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
+    <!-- Side padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QS-->
+    <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+    <!-- Icon size of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+    <!-- Size of text of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_size">12sp</dimen>
+    <!-- Margin between items in Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_items_margin">8dp</dimen>
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
@@ -1013,8 +1013,8 @@
     <dimen name="bubble_icon_inset">16dp</dimen>
     <!-- Padding around the view displayed when the bubble is expanded -->
     <dimen name="bubble_expanded_view_padding">8dp</dimen>
-    <!-- Default height of the expanded view shown when the bubble is expanded -->
-    <dimen name="bubble_expanded_default_height">400dp</dimen>
+    <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+    <dimen name="bubble_expanded_default_height">180dp</dimen>
     <!-- Height of the triangle that points to the expanded bubble -->
     <dimen name="bubble_pointer_height">4dp</dimen>
     <!-- Width of the triangle that points to the expanded bubble -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7d009b5..0fde2de 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1069,7 +1069,10 @@
     <string name="battery_saver_notification_action_text">Turn off Battery Saver</string>
 
     <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
-    <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string>
+    <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything on your screen including notifications, passwords, photos, messages and payment information.</string>
+
+    <!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] -->
+    <string name="media_projection_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> to record or cast your screen?</string>
 
     <!-- Media projection permission dialog permanent grant check box. [CHAR LIMIT=NONE] -->
     <string name="media_projection_remember_text">Don\'t show again</string>
@@ -1582,19 +1585,19 @@
     <!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
     <string name="inline_keep_showing">Keep showing these notifications?</string>
 
-    <!-- Notification inline controls: block notifications button -->
+    <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] -->
     <string name="inline_stop_button">Stop notifications</string>
 
     <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] -->
     <string name="inline_deliver_silently_button">Deliver Silently</string>
 
-    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] -->
     <string name="inline_block_button">Block</string>
 
-    <!-- Notification inline controls: keep getting notifications button -->
+    <!-- Notification inline controls: keep getting notifications button [CHAR_LIMIT=25] -->
     <string name="inline_keep_button">Keep showing</string>
 
-    <!-- Notification inline controls: minimize notifications button -->
+    <!-- Notification inline controls: minimize notifications button [CHAR_LIMIT=20] -->
     <string name="inline_minimize_button">Minimize</string>
 
     <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
@@ -2323,7 +2326,7 @@
     <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
     <string name="ongoing_privacy_dialog_ok">Got it</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]-->
     <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
 
     <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 2ff98ba..37abab9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -16,19 +16,32 @@
 
 package com.android.systemui.shared.recents;
 
+import android.graphics.Region;
+import android.os.Bundle;
 import android.view.MotionEvent;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
 oneway interface IOverviewProxy {
-    void onBind(in ISystemUiProxy sysUiProxy);
+
+    void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
+
+    void onInitialize(in Bundle params) = 12;
+
+
+    /**
+     * @deprecated
+     */
+    void onBind(in ISystemUiProxy sysUiProxy) = 0;
 
     /**
      * Called once immediately prior to the first onMotionEvent() call, providing a hint to the
      * target the initial source of the subsequent motion events.
      *
      * @param downHitTarget is one of the {@link NavigationBarCompat.HitTarget}s
+     *
+     * @deprecated
      */
-    void onPreMotionEvent(int downHitTarget);
+    void onPreMotionEvent(int downHitTarget) = 1;
 
     /**
      * Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
@@ -38,40 +51,48 @@
      * Quick scrub: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SCRUB_START, SCRUB_PROGRESS*, SCRUB_END
      *
      * Once quick scrub is sent, then no further motion events will be provided.
+     *
+     * @deprecated
      */
-    void onMotionEvent(in MotionEvent event);
+    void onMotionEvent(in MotionEvent event) = 2;
 
     /**
      * Sent when the user starts to actively scrub the nav bar to switch tasks. Once this event is
      * sent the caller will stop sending any motion events and will no longer preemptively cancel
      * any recents animations started as a part of the motion event handling.
+     *
+     * @deprecated
      */
-    void onQuickScrubStart();
+    void onQuickScrubStart() = 3;
 
     /**
      * Sent when the user stops actively scrubbing the nav bar to switch tasks.
+     *
+     * @deprecated
      */
-    void onQuickScrubEnd();
+    void onQuickScrubEnd() = 4;
 
     /**
      * Sent for each movement over the nav bar while the user is scrubbing it to switch tasks.
+     *
+     * @deprecated
      */
-    void onQuickScrubProgress(float progress);
+    void onQuickScrubProgress(float progress) = 5;
 
     /**
      * Sent when overview button is pressed to toggle show/hide of overview.
      */
-    void onOverviewToggle();
+    void onOverviewToggle() = 6;
 
     /**
      * Sent when overview is to be shown.
      */
-    void onOverviewShown(boolean triggeredFromAltTab);
+    void onOverviewShown(boolean triggeredFromAltTab) = 7;
 
     /**
      * Sent when overview is to be hidden.
      */
-    void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+    void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) = 8;
 
     /**
      * Sent when a user swipes up over the navigation bar to launch overview. Swipe up is determined
@@ -83,11 +104,13 @@
      * visible, this event will still be sent if user swipes up). When this signal is sent,
      * navigation bar will not handle any gestures such as quick scrub and the home button will
      * cancel (long) press.
+     *
+     * @deprecated
      */
-    void onQuickStep(in MotionEvent event);
+    void onQuickStep(in MotionEvent event) = 9;
 
     /**
      * Sent when there was an action on one of the onboarding tips view.
      */
-    void onTip(int actionType, int viewType);
+    void onTip(int actionType, int viewType) = 10;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
index f7ccb81..804f4f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import android.os.Bundle;
 import android.os.Looper;
 import android.util.Pair;
 import android.view.BatchedInputEventReceiver;
@@ -53,6 +54,16 @@
     }
 
     /**
+     * Creates a dispatcher from the extras received as part on onInitialize
+     */
+    public static InputEventReceiver fromBundle(Bundle params, String key,
+            Looper looper, Choreographer choreographer, InputEventListener listener) {
+
+        InputChannel channel = params.getParcelable(key);
+        return new InputEventReceiver(channel, looper, choreographer, listener);
+    }
+
+    /**
      * @see BatchedInputEventReceiver
      */
     public static class InputEventReceiver {
@@ -90,7 +101,7 @@
         private final InputChannel mInputChannel;
         private final InputEventSender mSender;
 
-        private InputEventDispatcher(InputChannel inputChannel, Looper looper) {
+        public InputEventDispatcher(InputChannel inputChannel, Looper looper) {
             mInputChannel = inputChannel;
             mSender = new InputEventSender(inputChannel, looper) { };
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index 69aea2c..b363b4a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -17,31 +17,15 @@
 package com.android.systemui.shared.system;
 
 import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-public class NavigationBarCompat {
-    /**
-     * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
-     * home button press/long press over are ignored and will start to drag when exceeded and the
-     * touch slop is when the respected operation will occur when exceeded. Touch slop must be
-     * larger than the drag slop.
-     */
-    public static int getQuickStepDragSlopPx() {
-        return convertDpToPixel(10);
-    }
+/**
+ * TODO: Remove this class
+ */
+public class NavigationBarCompat extends QuickStepContract {
 
-    public static int getQuickStepTouchSlopPx() {
-        return convertDpToPixel(24);
-    }
-
-    public static int getQuickScrubTouchSlopPx() {
-        return convertDpToPixel(24);
-    }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
@@ -75,8 +59,4 @@
      * Interaction type: show/hide the overview button while this service is connected to launcher
      */
     public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
-
-    private static int convertDpToPixel(float dp){
-        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
new file mode 100644
index 0000000..6d7abd0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import android.content.res.Resources;
+
+/**
+ * Various shared constants between Launcher and SysUI as part of quickstep
+ */
+public class QuickStepContract {
+
+    public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
+    public static final String KEY_EXTRA_INPUT_CHANNEL = "extra_input_channel";
+    public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
+    public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
+
+    /**
+     * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
+     * home button press/long press over are ignored and will start to drag when exceeded and the
+     * touch slop is when the respected operation will occur when exceeded. Touch slop must be
+     * larger than the drag slop.
+     */
+    public static int getQuickStepDragSlopPx() {
+        return convertDpToPixel(10);
+    }
+
+    public static int getQuickStepTouchSlopPx() {
+        return convertDpToPixel(24);
+    }
+
+    public static int getQuickScrubTouchSlopPx() {
+        return convertDpToPixel(24);
+    }
+
+    private static int convertDpToPixel(float dp) {
+        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 822920e..8de84bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,19 @@
 package com.android.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.transition.ChangeBounds;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionValues;
 import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -16,6 +25,7 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -28,6 +38,7 @@
  */
 public class KeyguardClockSwitch extends RelativeLayout {
 
+    private final Transition mTransition;
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -53,6 +64,10 @@
      * Maintain state so that a newly connected plugin can be initialized.
      */
     private float mDarkAmount;
+    /**
+     * If the Keyguard Slice has a header (big center-aligned text.)
+     */
+    private boolean mShowingHeader;
     private boolean mSupportsDarkText;
     private int[] mColorPalette;
 
@@ -98,6 +113,7 @@
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mTransition = new ClockBoundsTransition();
     }
 
     /**
@@ -286,6 +302,26 @@
         }
     }
 
+    /**
+     * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock
+     * in these cases.
+     */
+    public void setKeyguardShowingHeader(boolean hasHeader) {
+        if (mShowingHeader == hasHeader || hasCustomClock()) {
+            return;
+        }
+        mShowingHeader = hasHeader;
+
+        TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
+        int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
+                ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
+        int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
+                ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
+        mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+        mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
+                mClockView.getPaddingRight(), paddingBottom);
+    }
+
     @VisibleForTesting (otherwise = VisibleForTesting.NONE)
     ClockManager.ClockChangedListener getClockChangedListener() {
         return mClockChangedListener;
@@ -295,4 +331,54 @@
     StatusBarStateController.StateListener getStateListener() {
         return mStateListener;
     }
+
+    /**
+     * Special layout transition that scales the clock view as its bounds change, to make it look
+     * like the text is shrinking.
+     */
+    private class ClockBoundsTransition extends ChangeBounds {
+
+        ClockBoundsTransition() {
+            setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
+            setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        }
+
+        @Override
+        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+                TransitionValues endValues) {
+            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+            if (animator == null || startValues.view != mClockView) {
+                return animator;
+            }
+
+            ValueAnimator boundsAnimator = null;
+            if (animator instanceof AnimatorSet) {
+                Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
+                if (first instanceof ValueAnimator) {
+                    boundsAnimator = (ValueAnimator) first;
+                }
+            } else if (animator instanceof ValueAnimator) {
+                boundsAnimator = (ValueAnimator) animator;
+            }
+
+            if (boundsAnimator != null) {
+                float bigFontSize = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.widget_big_font_size);
+                float smallFontSize = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.widget_small_font_size);
+                float startScale = mShowingHeader
+                        ? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
+                boundsAnimator.addUpdateListener(animation -> {
+                    float scale = MathUtils.lerp(startScale, 1f /* stop */,
+                            animation.getAnimatedFraction());
+                    mClockView.setPivotX(mClockView.getWidth() / 2);
+                    mClockView.setPivotY(0);
+                    mClockView.setScaleX(scale);
+                    mClockView.setScaleY(scale);
+                });
+            }
+
+            return animator;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index bac7844..2040a76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -37,10 +37,10 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.animation.Animation;
 import android.widget.Button;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.Observer;
@@ -58,6 +58,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
+import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
@@ -78,6 +79,8 @@
 
     private final HashMap<View, PendingIntent> mClickActions;
     private Uri mKeyguardSliceUri;
+    @VisibleForTesting
+    TextView mTitle;
     private Row mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
@@ -91,6 +94,8 @@
     private Runnable mContentChangeListener;
     private Slice mSlice;
     private boolean mHasHeader;
+    private final int mRowWithHeaderPadding;
+    private final int mRowPadding;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -107,6 +112,9 @@
         tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
 
         mClickActions = new HashMap<>();
+        mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding);
+        mRowWithHeaderPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.header_subtitle_padding);
 
         LayoutTransition transition = new LayoutTransition();
         transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
@@ -117,13 +125,13 @@
         transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
         transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
         transition.setAnimateParentHierarchy(false);
-        transition.addTransitionListener(new SliceViewTransitionListener());
         setLayoutTransition(transition);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mTitle = findViewById(R.id.title);
         mRow = findViewById(R.id.row);
         mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
         mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
@@ -160,6 +168,7 @@
     private void showSlice() {
         Trace.beginSection("KeyguardSliceView#showSlice");
         if (mSlice == null) {
+            mTitle.setVisibility(GONE);
             mRow.setVisibility(GONE);
             mHasHeader = false;
             if (mContentChangeListener != null) {
@@ -170,8 +179,7 @@
 
         ListContent lc = new ListContent(getContext(), mSlice);
         SliceContent headerContent = lc.getHeader();
-        mHasHeader = headerContent != null
-                && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
+        mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
         List<SliceContent> subItems = new ArrayList<>();
         for (int i = 0; i < lc.getRowItems().size(); i++) {
             SliceContent subItem = lc.getRowItems().get(i);
@@ -181,12 +189,26 @@
                 subItems.add(subItem);
             }
         }
+        if (!mHasHeader) {
+            mTitle.setVisibility(GONE);
+        } else {
+            mTitle.setVisibility(VISIBLE);
+
+            RowContent header = lc.getHeader();
+            SliceItem mainTitle = header.getTitleItem();
+            CharSequence title = mainTitle != null ? mainTitle.getText() : null;
+            mTitle.setText(title);
+        }
 
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
         final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
+        LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
+        layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
+        mRow.setLayoutParams(layoutParams);
+
         for (int i = startIndex; i < subItemsCount; i++) {
             RowContent rc = (RowContent) subItems.get(i);
             SliceItem item = rc.getSliceItem();
@@ -250,6 +272,7 @@
 
     private void updateTextColors() {
         final int blendedColor = getTextColor();
+        mTitle.setTextColor(blendedColor);
         int childCount = mRow.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View v = mRow.getChildAt(i);
@@ -294,7 +317,10 @@
         setupUri(newValue);
     }
 
-    private void setupUri(String uriString) {
+    /**
+     * Sets the slice provider Uri.
+     */
+    public void setupUri(String uriString) {
         if (uriString == null) {
             uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
         }
@@ -512,29 +538,4 @@
             }
         }
     }
-
-    private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
-        @Override
-        public void startTransition(LayoutTransition transition, ViewGroup container, View view,
-                int transitionType) {
-            switch (transitionType) {
-                case  LayoutTransition.APPEARING:
-                    int translation = getResources().getDimensionPixelSize(
-                            R.dimen.pulsing_notification_appear_translation);
-                    view.setTranslationY(translation);
-                    view.animate()
-                            .translationY(0)
-                            .setDuration(DEFAULT_ANIM_DURATION)
-                            .setInterpolator(Interpolators.ALPHA_IN)
-                            .start();
-                    break;
-            }
-        }
-
-        @Override
-        public void endTransition(LayoutTransition transition, ViewGroup container, View view,
-                int transitionType) {
-
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index bb549ad..b0670fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,14 +16,11 @@
 
 package com.android.keyguard;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Paint;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -37,15 +34,12 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import com.google.android.collect.Sets;
@@ -54,14 +48,13 @@
 import java.util.TimeZone;
 
 public class KeyguardStatusView extends GridLayout implements
-        ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
+        ConfigurationController.ConfigurationListener {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
     private static final int MARQUEE_DELAY_MS = 2000;
 
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
-    private final float mSmallClockScale;
 
     private TextView mLogoutView;
     private KeyguardClockSwitch mClockView;
@@ -74,8 +67,6 @@
     private boolean mPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
-    private int mLastLayoutHeight;
-    private int mSmallClockPadding;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -135,8 +126,6 @@
         mIActivityManager = ActivityManager.getService();
         mLockPatternUtils = new LockPatternUtils(getContext());
         mHandler = new Handler(Looper.myLooper());
-        mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
-                / getResources().getDimension(R.dimen.widget_big_font_size);
         onDensityOrFontScaleChanged();
     }
 
@@ -189,9 +178,6 @@
         mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
         mTextColor = mClockView.getCurrentTextColor();
 
-        int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke);
-        mClockView.getPaint().setStrokeWidth(clockStroke);
-        mClockView.addOnLayoutChangeListener(this);
         mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
         onSliceContentChanged();
 
@@ -207,72 +193,20 @@
      * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
-        LinearLayout.LayoutParams layoutParams =
-                (LinearLayout.LayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0;
-        mClockView.setLayoutParams(layoutParams);
-    }
-
-    /**
-     * Animate clock when necessary.
-     */
-    @Override
-    public void onLayoutChange(View view, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        boolean smallClock = mKeyguardSlice.hasHeader();
-        int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight;
-        long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = smallClock ? 0 : duration / 4;
-
-        boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
-                && mKeyguardSlice.getLayoutTransition().isRunning();
-        if (view == mClockView) {
-            float clockScale = smallClock ? mSmallClockScale : 1;
-            Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
-            mClockView.animate().cancel();
-            if (shouldAnimate) {
-                mClockView.setY(oldTop + heightOffset);
-                mClockView.animate()
-                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                        .setDuration(duration)
-                        .setListener(new ClipChildrenAnimationListener())
-                        .setStartDelay(delay)
-                        .y(top)
-                        .scaleX(clockScale)
-                        .scaleY(clockScale)
-                        .withEndAction(() -> {
-                            mClockView.setStyle(style);
-                            mClockView.invalidate();
-                        })
-                        .start();
-            } else {
-                mClockView.setY(top);
-                mClockView.setScaleX(clockScale);
-                mClockView.setScaleY(clockScale);
-                mClockView.setStyle(style);
-                mClockView.invalidate();
-            }
-        }
+        mClockView.setKeyguardShowingHeader(mKeyguardSlice.hasHeader());
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mClockView.setPivotX(mClockView.getWidth() / 2);
-        mClockView.setPivotY(0);
-        mLastLayoutHeight = getHeight();
         layoutOwnerInfo();
     }
 
     @Override
     public void onDensityOrFontScaleChanged() {
-        mSmallClockPadding = getResources()
-                .getDimensionPixelSize(R.dimen.widget_small_clock_padding);
         if (mClockView != null) {
             mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
-            mClockView.getPaint().setStrokeWidth(
-                    getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke));
         }
         if (mOwnerInfo != null) {
             mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -461,24 +395,4 @@
             Log.e(TAG, "Failed to logout user", re);
         }
     }
-
-    private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
-            ViewClippingUtil.ClippingParameters {
-
-        ClipChildrenAnimationListener() {
-            ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
-                    this /* clippingParams */);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
-                    this /* clippingParams */);
-        }
-
-        @Override
-        public boolean shouldFinish(View view) {
-            return view == getParent();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f277c43..3ac7fd4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -724,6 +724,7 @@
     }
 
     private void handleFaceAuthFailed() {
+        setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9598142..c5dc324 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -15,41 +15,60 @@
  */
 package com.android.keyguard.clock;
 
+import android.annotation.Nullable;
+import android.app.WallpaperManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.DisplayMetrics;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
 
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManager.DockEventListener;
 import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionController.Extension;
+import com.android.systemui.util.InjectionInflationController;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
-import java.util.function.Consumer;
+import java.util.Map;
 import java.util.function.Supplier;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
 /**
- * Manages custom clock faces.
+ * Manages custom clock faces for AOD and lock screen.
  */
 @Singleton
 public final class ClockManager {
 
-    private final LayoutInflater mLayoutInflater;
-    private final ContentResolver mContentResolver;
-
     private final List<ClockInfo> mClockInfos = new ArrayList<>();
     /**
+     * Map from expected value stored in settings to supplier of custom clock face.
+     */
+    private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>();
+    @Nullable private ClockPlugin mCurrentClock;
+
+    private final ContentResolver mContentResolver;
+    private final SettingsWrapper mSettingsWrapper;
+    /**
      * Observe settings changes to know when to switch the clock face.
      */
     private final ContentObserver mContentObserver =
@@ -57,29 +76,48 @@
                 @Override
                 public void onChange(boolean selfChange) {
                     super.onChange(selfChange);
-                    if (mClockExtension != null) {
-                        mClockExtension.reload();
-                    }
+                    reload();
                 }
             };
-
-    private final ExtensionController mExtensionController;
     /**
-     * Used to select between plugin or default implementations of ClockPlugin interface.
+     * Observe changes to dock state to know when to switch the clock face.
      */
-    private Extension<ClockPlugin> mClockExtension;
+    private final DockEventListener mDockEventListener =
+            new DockEventListener() {
+                @Override
+                public void onEvent(int event) {
+                    mIsDocked = (event == DockManager.STATE_DOCKED
+                            || event == DockManager.STATE_DOCKED_HIDE);
+                    reload();
+                }
+            };
+    @Nullable private final DockManager mDockManager;
     /**
-     * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
+     * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
+     * to show.
      */
-    private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin;
+    private boolean mIsDocked;
 
     private final List<ClockChangedListener> mListeners = new ArrayList<>();
 
+    private final SysuiColorExtractor mColorExtractor;
+    private final int mWidth;
+    private final int mHeight;
+
     @Inject
-    public ClockManager(Context context, ExtensionController extensionController) {
-        mExtensionController = extensionController;
-        mLayoutInflater = LayoutInflater.from(context);
-        mContentResolver = context.getContentResolver();
+    public ClockManager(Context context, InjectionInflationController injectionInflater,
+            @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor) {
+        this(context, injectionInflater, dockManager, colorExtractor, context.getContentResolver(),
+                new SettingsWrapper(context.getContentResolver()));
+    }
+
+    ClockManager(Context context, InjectionInflationController injectionInflater,
+            @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor,
+            ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
+        mDockManager = dockManager;
+        mColorExtractor = colorExtractor;
+        mContentResolver = contentResolver;
+        mSettingsWrapper = settingsWrapper;
 
         Resources res = context.getResources();
         mClockInfos.add(ClockInfo.builder()
@@ -94,22 +132,35 @@
                 .setTitle(res.getString(R.string.clock_title_bubble))
                 .setId(BubbleClockController.class.getName())
                 .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail))
-                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_preview))
+                .setPreview(() -> getClockPreview(BubbleClockController.class.getName()))
                 .build());
         mClockInfos.add(ClockInfo.builder()
                 .setName("stretch")
                 .setTitle(res.getString(R.string.clock_title_stretch))
                 .setId(StretchAnalogClockController.class.getName())
                 .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail))
-                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_preview))
+                .setPreview(() -> getClockPreview(StretchAnalogClockController.class.getName()))
                 .build());
         mClockInfos.add(ClockInfo.builder()
                 .setName("type")
                 .setTitle(res.getString(R.string.clock_title_type))
                 .setId(TypeClockController.class.getName())
                 .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
-                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
+                .setPreview(() -> getClockPreview(TypeClockController.class.getName()))
                 .build());
+
+        LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
+        mClocks.put(BubbleClockController.class.getName(),
+                () -> BubbleClockController.build(layoutInflater));
+        mClocks.put(StretchAnalogClockController.class.getName(),
+                () -> StretchAnalogClockController.build(layoutInflater));
+        mClocks.put(TypeClockController.class.getName(),
+                () -> TypeClockController.build(layoutInflater));
+
+        // Store the size of the display for generation of clock preview.
+        DisplayMetrics dm = res.getDisplayMetrics();
+        mWidth = dm.widthPixels;
+        mHeight = dm.heightPixels;
     }
 
     /**
@@ -120,9 +171,7 @@
             register();
         }
         mListeners.add(listener);
-        if (mClockExtension != null) {
-            mClockExtension.reload();
-        }
+        reload();
     }
 
     /**
@@ -142,7 +191,66 @@
         return mClockInfos;
     }
 
-    private void setClockPlugin(ClockPlugin plugin) {
+    /**
+     * Get the current clock.
+     * @returns current custom clock or null for default.
+     */
+    @Nullable
+    ClockPlugin getCurrentClock() {
+        return mCurrentClock;
+    }
+
+    @VisibleForTesting
+    boolean isDocked() {
+        return mIsDocked;
+    }
+
+    @VisibleForTesting
+    ContentObserver getContentObserver() {
+        return mContentObserver;
+    }
+
+    /**
+     * Generate a realistic preview of a clock face.
+     * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}.
+     *        Returns null if clockId is not found.
+     */
+    @Nullable
+    private Bitmap getClockPreview(String clockId) {
+        Supplier<ClockPlugin> supplier = mClocks.get(clockId);
+        if (supplier == null) {
+            return null;
+        }
+        ClockPlugin plugin = supplier.get();
+
+        // Use the big clock view for the preview
+        View clockView = plugin.getBigClockView();
+        if (clockView == null) {
+            return null;
+        }
+
+        // Initialize state of plugin before generating preview.
+        plugin.setDarkAmount(1f);
+        plugin.setTextColor(Color.WHITE);
+
+        ColorExtractor.GradientColors colors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
+                true);
+        plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+        plugin.dozeTimeTick();
+
+        // Draw clock view hierarchy to canvas.
+        Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
+        clockView.layout(0, 0, mWidth, mHeight);
+        canvas.drawColor(Color.BLACK);
+        clockView.draw(canvas);
+
+        return bitmap;
+    }
+
+    private void notifyClockChanged(ClockPlugin plugin) {
         for (int i = 0; i < mListeners.size(); i++) {
             // It probably doesn't make sense to supply the same plugin instances to multiple
             // listeners. This should be fine for now since there is only a single listener.
@@ -154,39 +262,48 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
                 false, mContentObserver);
-        mClockExtension = mExtensionController.newExtension(ClockPlugin.class)
-            .withPlugin(ClockPlugin.class)
-            .withCallback(mClockPluginConsumer)
-            // Using withDefault even though this isn't the default as a workaround.
-            // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin
-            // instance based off of the value of a setting. Since multiple "default"
-            // can be provided, using a supplier that changes the settings value.
-            // A null return will cause Extension#reload to look at the next "default"
-            // supplier.
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        BubbleClockController.class.getName(),
-                            () -> BubbleClockController.build(mLayoutInflater)))
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        StretchAnalogClockController.class.getName(),
-                            () -> StretchAnalogClockController.build(mLayoutInflater)))
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        TypeClockController.class.getName(),
-                            () -> TypeClockController.build(mLayoutInflater)))
-            .build();
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
+                false, mContentObserver);
+        if (mDockManager != null) {
+            mDockManager.addListener(mDockEventListener);
+        }
     }
 
     private void unregister() {
         mContentResolver.unregisterContentObserver(mContentObserver);
-        mClockExtension.destroy();
+        if (mDockManager != null) {
+            mDockManager.removeListener(mDockEventListener);
+        }
+    }
+
+    private void reload() {
+        mCurrentClock = getClockPlugin();
+        notifyClockChanged(mCurrentClock);
+    }
+
+    private ClockPlugin getClockPlugin() {
+        ClockPlugin plugin = null;
+        if (mIsDocked) {
+            final String name = mSettingsWrapper.getDockedClockFace();
+            if (name != null) {
+                Supplier<ClockPlugin> supplier = mClocks.get(name);
+                if (supplier != null) {
+                    plugin = supplier.get();
+                    if (plugin != null) {
+                        return plugin;
+                    }
+                }
+            }
+        }
+        final String name = mSettingsWrapper.getLockScreenCustomClockFace();
+        if (name != null) {
+            Supplier<ClockPlugin> supplier = mClocks.get(name);
+            if (supplier != null) {
+                plugin = supplier.get();
+            }
+        }
+        return plugin;
     }
 
     /**
@@ -200,44 +317,4 @@
          */
         void onClockChanged(ClockPlugin clock);
     }
-
-    /**
-     * Supplier that only gets an instance when a settings value matches expected value.
-     */
-    private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
-
-        private final ContentResolver mContentResolver;
-        private final String mKey;
-        private final String mValue;
-        private final Supplier<ClockPlugin> mSupplier;
-
-        /**
-         * Constructs a supplier that changes secure setting key against value.
-         *
-         * @param contentResolver Used to look up settings value.
-         * @param key Settings key.
-         * @param value If the setting matches this values that get supplies a ClockPlugin
-         *        instance.
-         * @param supplier Supplier of ClockPlugin instance, only used if the setting
-         *        matches value.
-         */
-        SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
-                Supplier<ClockPlugin> supplier) {
-            mContentResolver = contentResolver;
-            mKey = key;
-            mValue = value;
-            mSupplier = supplier;
-        }
-
-        /**
-         * Returns null if the settings value doesn't match the expected value.
-         *
-         * A null return causes Extension#reload to skip this supplier and move to the next.
-         */
-        @Override
-        public ClockPlugin get() {
-            final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
-            return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
index e35cf11..2275380 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
@@ -37,7 +37,7 @@
 
     private ImageView mHourHand;
     private ImageView mMinuteHand;
-    private Calendar mTime;
+    private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
     private String mDescFormat;
     private TimeZone mTimeZone;
 
@@ -51,7 +51,6 @@
 
     public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mTime = Calendar.getInstance();
         mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
     }
 
@@ -98,7 +97,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
         onTimeChanged();
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
new file mode 100644
index 0000000..58e1155
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Wrapper around Settings used for testing.
+ */
+public class SettingsWrapper {
+
+    private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE;
+    private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE;
+
+    private ContentResolver mContentResolver;
+
+    public SettingsWrapper(ContentResolver contentResolver) {
+        mContentResolver = contentResolver;
+    }
+
+    /**
+     * Gets the value stored in settings for the custom clock face.
+     */
+    public String getLockScreenCustomClockFace() {
+        return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE);
+    }
+
+    /**
+     * Gets the value stored in settings for the clock face to use when docked.
+     */
+    public String getDockedClockFace() {
+        return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
index 3c9a4f8..34c855b 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
@@ -37,7 +37,7 @@
 
     private final Paint mHourPaint = new Paint();
     private final Paint mMinutePaint = new Paint();
-    private Calendar mTime;
+    private Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
     private TimeZone mTimeZone;
 
     public StretchAnalogClock(Context context) {
@@ -138,7 +138,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
         onTimeChanged();
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
index 6f1b59c..7bce3c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
@@ -44,7 +44,7 @@
     private final String[] mHours;
     private final String[] mMinutes;
     private int mAccentColor;
-    private Calendar mTime;
+    private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
     private String mDescFormat;
     private TimeZone mTimeZone;
 
@@ -58,7 +58,6 @@
 
     public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mTime = Calendar.getInstance();
         mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
         mResources = context.getResources();
         mHours = mResources.getStringArray(R.array.type_clock_hours);
@@ -111,12 +110,13 @@
      */
     public void setClockColor(int color) {
         mAccentColor = color;
+        onTimeChanged();
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
         onTimeChanged();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index ce9c637..3c6f081 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -21,11 +21,13 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
@@ -194,6 +196,12 @@
     /**
      */
     @Binds
+    public abstract StatusBarStateController provideStatusBarStateController(
+            StatusBarStateControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
     public abstract StatusBarIconController provideStatusBarIconController(
             StatusBarIconControllerImpl controllerImpl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index e28aa9d..2a1d066 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -23,7 +23,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -59,7 +58,6 @@
     private int mEndPoint;
     private boolean mEdgeBleed;
     private boolean mRoundedDivider;
-    private int mRotation = ROTATION_NONE;
     private boolean mRotatedBackground;
     private boolean mSwapOrientation = true;
 
@@ -89,7 +87,7 @@
     }
 
     @Override
-    public ViewGroup getParentView(boolean separated, int index) {
+    public ViewGroup getParentView(boolean separated, int index, boolean reverse) {
         if (separated) {
             return getSeparatedView();
         } else {
@@ -174,7 +172,6 @@
                 mSeparatedView.setBackground(mSeparatedViewBackground);
                 updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
                 mOldHeight = mList.getMeasuredHeight();
-                updateRotation();
             } else {
                 return;
             }
@@ -188,25 +185,13 @@
         post(() -> updatePosition());
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateRotation();
-    }
-
     public void setSwapOrientation(boolean swapOrientation) {
         mSwapOrientation = swapOrientation;
     }
 
-    private void updateRotation() {
-        int rotation = RotationUtils.getRotation(getContext());
-        if (rotation != mRotation) {
-            rotate(mRotation, rotation);
-            mRotation = rotation;
-        }
-    }
-
-    private void rotate(int from, int to) {
+    @Override
+    protected void rotate(int from, int to) {
+        super.rotate(from, to);
         if (from != ROTATION_NONE && to != ROTATION_NONE) {
             // Rather than handling this confusing case, just do 2 rotations.
             rotate(from, ROTATION_NONE);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2aecc24..7e645ab 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -28,7 +28,9 @@
 import android.graphics.RectF;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
+import android.opengl.GLSurfaceView;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
@@ -39,6 +41,7 @@
 import android.view.SurfaceHolder;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -73,10 +76,78 @@
 
     @Override
     public Engine onCreateEngine() {
-        mEngine = new DrawableEngine();
-        return mEngine;
+        if (Build.IS_DEBUGGABLE) {
+            Log.v(TAG, "We are using GLEngine");
+        }
+        return new GLEngine(this);
     }
 
+    class GLEngine extends Engine {
+        private GLWallpaperSurfaceView mWallpaperSurfaceView;
+
+        GLEngine(Context context) {
+            mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
+            mWallpaperSurfaceView.setRenderer(
+                    new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
+            mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+            setOffsetNotificationsEnabled(true);
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
+            if (mWallpaperSurfaceView != null) {
+                mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration);
+            }
+        }
+
+        @Override
+        public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
+                float yOffsetStep, int xPixelOffset, int yPixelOffset) {
+            if (mWallpaperSurfaceView != null) {
+                mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
+            }
+        }
+
+        private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
+            private WallpaperStatusListener mWallpaperChangedListener;
+
+            GLWallpaperSurfaceView(Context context) {
+                super(context);
+                setEGLContextClientVersion(2);
+            }
+
+            @Override
+            public SurfaceHolder getHolder() {
+                return getSurfaceHolder();
+            }
+
+            @Override
+            public void setRenderer(Renderer renderer) {
+                super.setRenderer(renderer);
+                mWallpaperChangedListener = (WallpaperStatusListener) renderer;
+            }
+
+            private void notifyAmbientModeChanged(boolean inAmbient, long duration) {
+                if (mWallpaperChangedListener != null) {
+                    mWallpaperChangedListener.onAmbientModeChanged(inAmbient, duration);
+                }
+            }
+
+            private void notifyOffsetsChanged(float xOffset, float yOffset) {
+                if (mWallpaperChangedListener != null) {
+                    mWallpaperChangedListener.onOffsetsChanged(
+                            xOffset, yOffset, getHolder().getSurfaceFrame());
+                }
+            }
+
+            @Override
+            public void render() {
+                requestRender();
+            }
+        }
+    }
+
+    // TODO: Remove this engine, tracking on b/123617158.
     class DrawableEngine extends Engine {
         private final Runnable mUnloadWallpaperCallback = () -> {
             unloadWallpaper(false /* forgetSize */);
@@ -564,4 +635,35 @@
             }
         }
     }
+
+    /**
+     * A listener to trace status of image wallpaper.
+     */
+    public interface WallpaperStatusListener {
+
+        /**
+         * Called back while ambient mode changes.
+         * @param inAmbientMode true if is in ambient mode, false otherwise.
+         * @param duration the duration of animation.
+         */
+        void onAmbientModeChanged(boolean inAmbientMode, long duration);
+
+        /**
+         * Called back while wallpaper offsets.
+         * @param xOffset The offset portion along x.
+         * @param yOffset The offset portion along y.
+         */
+        void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
+    }
+
+    /**
+     * An abstraction for view of GLRenderer.
+     */
+    public interface ImageGLView {
+
+        /**
+         * Ask the view to render.
+         */
+        void render();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index 85265f4..8c49d56 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -17,11 +17,14 @@
 package com.android.systemui;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import com.android.systemui.util.leak.RotationUtils;
+
 /**
  * Layout class representing the Global Actions menu which appears when the power button is held.
  */
@@ -32,8 +35,12 @@
     protected int mExpectedSeparatedItemCount;
     protected int mExpectedListItemCount;
 
+    protected int mRotation;
+    protected RotationListener mRotationListener;
+
     public MultiListLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mRotation = RotationUtils.getRotation(context);
     }
 
     protected abstract ViewGroup getSeparatedView();
@@ -50,10 +57,12 @@
      * @param separated Whether or not this index refers to a position in the separated or list
      *                  container.
      * @param index The index of the item within the container.
+     * @param reverse If the MultiListLayout contains sub-lists within the list container, reverse
+     *                the order that they are filled.
      * @return The parent ViewGroup which will be used to contain the specified item
      * after it has been added to the layout.
      */
-    public abstract ViewGroup getParentView(boolean separated, int index);
+    public abstract ViewGroup getParentView(boolean separated, int index, boolean reverse);
 
     /**
      * Sets the divided view, which may have a differently-colored background.
@@ -111,6 +120,26 @@
         setFocusable(true);
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        int newRotation = RotationUtils.getRotation(mContext);
+        if (newRotation != mRotation) {
+            rotate(mRotation, newRotation);
+            mRotation = newRotation;
+        }
+    }
+
+    protected void rotate(int from, int to) {
+        if (mRotationListener != null) {
+            mRotationListener.onRotate(from, to);
+        }
+    }
+
+    public void setRotationListener(RotationListener listener) {
+        mRotationListener = listener;
+    }
+
     /**
      * Retrieve the MultiListLayout associated with the given view.
      */
@@ -121,4 +150,11 @@
         }
         return null;
     }
+
+    /**
+     * Interface to provide callbacks which trigger when this list detects a rotation.
+     */
+    public interface RotationListener {
+        void onRotate(int from, int to);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 755d6fc..6bb4fb5 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.keyguard.ViewMediatorCallback;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -40,8 +41,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -141,7 +142,7 @@
             StatusBar statusBar, StatusBarStateController statusBarStateController,
             NotificationListener listener) {
         return new NotificationIconAreaController(context, statusBar, statusBarStateController,
-                listener);
+                listener, Dependency.get(NotificationMediaManager.class));
     }
 
     public KeyguardIndicationController createKeyguardIndicationController(Context context,
@@ -153,15 +154,6 @@
         return new VolumeDialogComponent(systemUi, context);
     }
 
-    /**
-     * Provides status bar state controller implementation
-     */
-    @Singleton
-    @Provides
-    public StatusBarStateController provideStatusBarStateController(Context context) {
-        return new StatusBarStateControllerImpl();
-    }
-
     @Singleton
     @Provides
     public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
@@ -228,6 +220,16 @@
         return SysUiServiceProvider.getComponent(context, StatusBar.class);
     }
 
+    /**
+     * Provides DockManager.
+     */
+    @Singleton
+    @Provides
+    @Nullable
+    public DockManager providesDockManager(Context context) {
+        return SysUiServiceProvider.getComponent(context, DockManager.class);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 4d70890..83398cf 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -122,23 +122,9 @@
                     }
 
                     @Override
-                    public void onTranscriptionUpdate(String transcription) {
+                    public void onSetUiHints(Bundle hints) {
                         if (VERBOSE) {
-                            Log.v(TAG, "Transcription Updated: \"" + transcription + "\"");
-                        }
-                    }
-
-                    @Override
-                    public void onTranscriptionComplete(boolean immediate) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "Transcription complete (immediate=" + immediate + ")");
-                        }
-                    }
-
-                    @Override
-                    public void onVoiceStateChange(int state) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "Voice state is now " + state);
+                            Log.v(TAG, "UI hints received");
                         }
                     }
                 });
@@ -290,10 +276,6 @@
         return mAssistUtils.isSessionRunning();
     }
 
-    public void destroy() {
-        mWindowManager.removeViewImmediate(mView);
-    }
-
     private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
         replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
                 isService);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
new file mode 100644
index 0000000..4fe09a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+
+import android.view.LayoutInflater;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Encapsulates the data and UI elements of a bubble.
+ */
+class Bubble {
+
+    public BubbleView iconView;
+    public BubbleExpandedView expandedView;
+    public String key;
+    public NotificationEntry entry;
+
+    Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
+            BubbleExpandedView.OnBubbleBlockedListener listener) {
+        entry = e;
+        key = entry.key;
+
+        iconView = (BubbleView) inflater.inflate(
+                R.layout.bubble_view, stackView, false /* attachToRoot */);
+        iconView.setNotif(entry);
+
+        expandedView = (BubbleExpandedView) inflater.inflate(
+                R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
+        expandedView.setEntry(entry, stackView);
+
+        expandedView.setOnBlockedListener(listener);
+    }
+
+    public void setEntry(NotificationEntry entry) {
+        key = entry.key;
+        iconView.update(entry);
+        expandedView.update(entry);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 41bc1b2..0832296 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.bubbles;
 
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -33,21 +29,14 @@
 import android.app.IActivityTaskManager;
 import android.app.INotificationManager;
 import android.app.Notification;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.StatsLog;
 import android.view.Display;
-import android.view.LayoutInflater;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.annotation.MainThread;
@@ -63,13 +52,10 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -105,11 +91,9 @@
     private final BubbleTaskStackListener mTaskStackListener;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
-    private LayoutInflater mInflater;
 
-    private final Map<String, BubbleView> mBubbles = new HashMap<>();
+    private BubbleData mBubbleData;
     private BubbleStackView mStackView;
-    private final Point mDisplaySize;
 
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
@@ -166,12 +150,9 @@
     }
 
     @Inject
-    public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
+    public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
+            BubbleData data) {
         mContext = context;
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mDisplaySize = new Point();
-        wm.getDefaultDisplay().getSize(mDisplaySize);
-        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -190,6 +171,8 @@
         mActivityTaskManager = ActivityTaskManager.getService();
         mTaskStackListener = new BubbleTaskStackListener();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+
+        mBubbleData = data;
     }
 
     /**
@@ -219,8 +202,11 @@
      * screen (e.g. if on AOD).
      */
     public boolean hasBubbles() {
-        for (BubbleView bv : mBubbles.values()) {
-            if (!bv.getEntry().isBubbleDismissed()) {
+        if (mStackView == null) {
+            return false;
+        }
+        for (Bubble bubble : mBubbleData.getBubbles()) {
+            if (!bubble.entry.isBubbleDismissed()) {
                 return true;
             }
         }
@@ -250,10 +236,6 @@
         if (mStackView == null) {
             return;
         }
-        Set<String> keys = mBubbles.keySet();
-        for (String key: keys) {
-            mBubbles.get(key).getEntry().setBubbleDismissed(true);
-        }
         mStackView.stackDismissed();
 
         updateVisibility();
@@ -261,19 +243,28 @@
     }
 
     /**
+     * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
+     * is forwarded a back key down/up pair.
+     */
+    public void performBackPressIfNeeded() {
+        if (mStackView != null) {
+            mStackView.performBackPressIfNeeded();
+        }
+    }
+
+    /**
      * Adds or updates a bubble associated with the provided notification entry.
      *
      * @param notif the notification associated with this bubble.
      * @param updatePosition whether this update should promote the bubble to the top of the stack.
      */
     public void updateBubble(NotificationEntry notif, boolean updatePosition) {
-        if (mBubbles.containsKey(notif.key)) {
+        if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
             // It's an update
-            BubbleView bubble = mBubbles.get(notif.key);
-            mStackView.updateBubble(bubble, notif, updatePosition);
+            mStackView.updateBubble(notif, updatePosition);
         } else {
             if (mStackView == null) {
-                mStackView = new BubbleStackView(mContext);
+                mStackView = new BubbleStackView(mContext, mBubbleData);
                 ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
                 // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
                 // between bubble and the shade
@@ -286,15 +277,7 @@
                 mStackView.setOnBlockedListener(this);
             }
             // It's new
-            BubbleView bubble = (BubbleView) mInflater.inflate(
-                    R.layout.bubble_view, mStackView, false /* attachToRoot */);
-            bubble.setNotif(notif);
-            PendingIntent bubbleIntent = getValidBubbleIntent(notif);
-            if (bubbleIntent != null) {
-                bubble.setBubbleIntent(bubbleIntent);
-            }
-            mBubbles.put(bubble.getKey(), bubble);
-            mStackView.addBubble(bubble);
+            mStackView.addBubble(notif);
         }
         updateVisibility();
     }
@@ -306,25 +289,18 @@
      */
     @MainThread
     void removeBubble(String key) {
-        BubbleView bv = mBubbles.remove(key);
-        if (mStackView != null && bv != null) {
-            mStackView.removeBubble(bv);
-            bv.destroyActivityView(mStackView);
+        if (mStackView != null) {
+            mStackView.removeBubble(key);
         }
-
-        NotificationEntry entry = bv != null ? bv.getEntry() : null;
-        if (entry != null) {
-            entry.setBubbleDismissed(true);
-            mNotificationEntryManager.updateNotifications();
-        }
+        mNotificationEntryManager.updateNotifications();
         updateVisibility();
     }
 
     @Override
     public void onBubbleBlocked(NotificationEntry entry) {
-        Object[] bubbles = mBubbles.values().toArray();
+        Object[] bubbles = mBubbleData.getBubbles().toArray();
         for (int i = 0; i < bubbles.length; i++) {
-            NotificationEntry e = ((BubbleView) bubbles[i]).getEntry();
+            NotificationEntry e = ((Bubble) bubbles[i]).entry;
             boolean samePackage = entry.notification.getPackageName().equals(
                     e.notification.getPackageName());
             if (samePackage) {
@@ -349,8 +325,7 @@
         }
 
         @Override
-        public void onEntryInflated(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+        public void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
@@ -368,10 +343,8 @@
                     && alertAgain(entry, entry.notification.getNotification())) {
                 entry.setShowInShadeWhenBubble(true);
                 entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
-                if (mBubbles.containsKey(entry.key)) {
-                    mBubbles.get(entry.key).updateDotVisibility();
-                }
                 updateBubble(entry, true /* updatePosition */);
+                mStackView.updateDotVisibility(entry.key);
             }
         }
 
@@ -383,8 +356,8 @@
                 return;
             }
             entry.setShowInShadeWhenBubble(false);
-            if (mBubbles.containsKey(entry.key)) {
-                mBubbles.get(entry.key).updateDotVisibility();
+            if (mStackView != null) {
+                mStackView.updateDotVisibility(entry.key);
             }
             if (!removedByUser) {
                 // This was a cancel so we should remove the bubble
@@ -441,65 +414,6 @@
         return mStackView;
     }
 
-    @Nullable
-    private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
-        Notification notification = notif.notification.getNotification();
-        String packageName = notif.notification.getPackageName();
-        Notification.BubbleMetadata data = notif.getBubbleMetadata();
-        if (data != null && canLaunchInActivityView(data.getIntent(),
-                true /* enable logging for bubbles */, packageName)) {
-            return data.getIntent();
-        }
-        if (shouldUseContentIntent(mContext)
-                && canLaunchInActivityView(notification.contentIntent,
-                false /* disable logging for notifications */, packageName)) {
-            Log.d(TAG, "[addBubble " + notif.key
-                    + "]: No appOverlayIntent, using contentIntent.");
-            return notification.contentIntent;
-        }
-        Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
-        return null;
-    }
-
-    /**
-     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
-     *
-     * @param intent the pending intent of the bubble.
-     * @param enableLogging whether bubble developer error should be logged.
-     * @param packageName the notification package name for this bubble.
-     * @return
-     */
-    private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
-                                            String packageName) {
-        if (intent == null) {
-            return false;
-        }
-        ActivityInfo info =
-                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
-        if (info == null) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
-            }
-            return false;
-        }
-        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
-            }
-            return false;
-        }
-        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
-            }
-            return false;
-        }
-        return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
-    }
-
     /**
      * Whether the notification has been developer configured to bubble and is allowed by the user.
      */
@@ -620,7 +534,7 @@
                 ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
 
-    private static boolean shouldUseContentIntent(Context context) {
+    static boolean shouldUseContentIntent(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
new file mode 100644
index 0000000..5002f5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Keeps track of active bubbles.
+ */
+@Singleton
+class BubbleData {
+
+    private HashMap<String, Bubble> mBubbles = new HashMap<>();
+
+    @Inject
+    BubbleData() {}
+
+    /**
+     * The set of bubbles.
+     */
+    public Collection<Bubble> getBubbles() {
+        return mBubbles.values();
+    }
+
+    @Nullable
+    public Bubble getBubble(String key) {
+        return mBubbles.get(key);
+    }
+
+    public void addBubble(Bubble b) {
+        mBubbles.put(b.key, b);
+    }
+
+    @Nullable
+    public Bubble removeBubble(String key) {
+        return mBubbles.remove(key);
+    }
+
+    public void updateBubble(String key, NotificationEntry newEntry) {
+        Bubble oldBubble = mBubbles.get(key);
+        if (oldBubble != null) {
+            oldBubble.setEntry(newEntry);
+        }
+    }
+
+    public void clear() {
+        mBubbles.clear();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 976a766..c2327ad 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,19 +16,28 @@
 
 package com.android.systemui.bubbles;
 
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
+
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
+import android.app.ActivityView;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.RemoteException;
@@ -39,16 +48,21 @@
 import android.util.Log;
 import android.util.StatsLog;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 
 /**
  * Container for the expanded bubble view, handles rendering the caret and header of the view.
@@ -68,8 +82,15 @@
     // Permission view
     private View mPermissionView;
 
-    // The view that is being displayed for the expanded state
-    private View mExpandedView;
+    // Views for expanded state
+    private ExpandableNotificationRow mNotifRow;
+    private ActivityView mActivityView;
+
+    private boolean mActivityViewReady = false;
+    private PendingIntent mBubbleIntent;
+
+    private int mMinHeight;
+    private int mHeaderHeight;
 
     private NotificationEntry mEntry;
     private PackageManager mPm;
@@ -77,11 +98,40 @@
     private Drawable mAppIcon;
 
     private INotificationManager mNotificationManagerService;
+    private BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
-    // Need reference to let it know to collapse when new task is launched
     private BubbleStackView mStackView;
 
-    private OnBubbleBlockedListener mOnBubbleBlockedListener;
+    private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener;
+
+    private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
+        @Override
+        public void onActivityViewReady(ActivityView view) {
+            if (!mActivityViewReady) {
+                mActivityViewReady = true;
+                mActivityView.startActivity(mBubbleIntent);
+            }
+        }
+
+        @Override
+        public void onActivityViewDestroyed(ActivityView view) {
+            mActivityViewReady = false;
+        }
+
+        /**
+         * This is only called for tasks on this ActivityView, which is also set to
+         * single-task mode -- meaning never more than one task on this display. If a task
+         * is being removed, it's the top Activity finishing and this bubble should
+         * be removed or collapsed.
+         */
+        @Override
+        public void onTaskRemovalStarted(int taskId) {
+            if (mEntry != null) {
+                // Must post because this is called from a binder thread.
+                post(() -> mBubbleController.removeBubble(mEntry.key));
+            }
+        }
+    };
 
     public BubbleExpandedView(Context context) {
         this(context, null);
@@ -99,6 +149,8 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mPm = context.getPackageManager();
+        mMinHeight = getResources().getDimensionPixelSize(
+                R.dimen.bubble_expanded_default_height);
         try {
             mNotificationManagerService = INotificationManager.Stub.asInterface(
                     ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
@@ -142,6 +194,8 @@
         viewWrapper.setLayoutTransition(transition);
         viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
 
+        mHeaderHeight = getContext().getResources().getDimensionPixelSize(
+                R.dimen.bubble_expanded_header_height);
         mHeaderView = findViewById(R.id.header_layout);
         mHeaderTextView = findViewById(R.id.header_text);
         mDeepLinkIcon = findViewById(R.id.deep_link_button);
@@ -152,6 +206,28 @@
         mPermissionView = findViewById(R.id.permission_layout);
         findViewById(R.id.no_bubbles_button).setOnClickListener(this);
         findViewById(R.id.yes_bubbles_button).setOnClickListener(this);
+
+        mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
+                true /* singleTaskInstance */);
+        addView(mActivityView);
+
+        mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
+            ActivityView activityView = (ActivityView) view;
+            // Here we assume that the position of the ActivityView on the screen
+            // remains regardless of IME status. When we move ActivityView, the
+            // forwardedInsets should be computed not against the current location
+            // and size, but against the post-moved location and size.
+            Point displaySize = new Point();
+            view.getContext().getDisplay().getSize(displaySize);
+            int[] windowLocation = view.getLocationOnScreen();
+            final int windowBottom = windowLocation[1] + view.getHeight();
+            final int keyboardHeight = insets.getSystemWindowInsetBottom()
+                    - insets.getStableInsetBottom();
+            final int insetsBottom = Math.max(0,
+                    windowBottom + keyboardHeight - displaySize.y);
+            activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
+            return view.onApplyWindowInsets(insets);
+        });
     }
 
     /**
@@ -189,6 +265,29 @@
         }
         updateHeaderView();
         updatePermissionView();
+        updateExpandedView();
+    }
+
+    /**
+     * Lets activity view know it should be shown / populated.
+     */
+    public void populateActivityView() {
+        mActivityView.setCallback(mStateCallback);
+    }
+
+    /**
+     * Updates the entry backing this view. This will not re-populate ActivityView, it will
+     * only update the deep-links in the header, the title, and the height of the view.
+     */
+    public void update(NotificationEntry entry) {
+        if (entry.key.equals(mEntry.key)) {
+            mEntry = entry;
+            updateHeaderView();
+            updateHeight();
+        } else {
+            Log.w(TAG, "Trying to update entry with different key, new entry: "
+                    + entry.key + " old entry: " + mEntry.key);
+        }
     }
 
     private void updateHeaderView() {
@@ -225,6 +324,56 @@
         }
     }
 
+    private void updateExpandedView() {
+        mBubbleIntent = getBubbleIntent(mEntry);
+        if (mBubbleIntent != null) {
+            if (mNotifRow != null) {
+                // Clear out the row if we had it previously
+                removeView(mNotifRow);
+                mNotifRow = null;
+            }
+            mActivityView.setVisibility(VISIBLE);
+        } else {
+            // Hide activity view if we had it previously
+            mActivityView.setVisibility(GONE);
+
+            // Use notification view
+            mNotifRow = mEntry.getRow();
+            addView(mNotifRow);
+        }
+        updateView();
+    }
+
+    boolean performBackPressIfNeeded() {
+        if (mActivityView == null || !usingActivityView()) {
+            return false;
+        }
+        mActivityView.performBackPress();
+        return true;
+    }
+
+    void updateHeight() {
+        if (usingActivityView()) {
+            Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+            int desiredHeight;
+            if (data == null) {
+                // This is a contentIntent based bubble, lets allow it to be the max height
+                // as it was forced into this mode and not prepared to be small
+                desiredHeight = mStackView.getMaxExpandedHeight();
+            } else {
+                desiredHeight = data.getDesiredHeight() > 0
+                        ? data.getDesiredHeight()
+                        : mMinHeight;
+            }
+            int max = mStackView.getMaxExpandedHeight() - mHeaderHeight;
+            int height = Math.min(desiredHeight, max);
+            height = Math.max(height, mMinHeight);
+            LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
+            lp.height = height;
+            mActivityView.setLayoutParams(lp);
+        }
+    }
+
     @Override
     public void onClick(View view) {
         if (mEntry == null) {
@@ -279,36 +428,88 @@
     }
 
     /**
+     * Update appearance of the expanded view being displayed.
+     */
+    public void updateView() {
+        if (usingActivityView()
+                && mActivityView.getVisibility() == VISIBLE
+                && mActivityView.isAttachedToWindow()) {
+            mActivityView.onLocationChanged();
+        } else if (mNotifRow != null) {
+            applyRowState(mNotifRow);
+        }
+        updateHeight();
+    }
+
+    /**
      * Set the x position that the tip of the triangle should point to.
      */
-    public void setPointerPosition(int x) {
+    public void setPointerPosition(float x) {
         // Adjust for the pointer size
-        x -= (mPointerView.getWidth() / 2);
+        x -= (mPointerView.getWidth() / 2f);
         mPointerView.setTranslationX(x);
     }
 
     /**
-     * Set the view to display for the expanded state. Passing null will clear the view.
+     * Removes and releases an ActivityView if one was previously created for this bubble.
      */
-    public void setExpandedView(View view) {
-        if (mExpandedView == view) {
+    public void destroyActivityView(ViewGroup tmpParent) {
+        if (mActivityView == null) {
             return;
         }
-        if (mExpandedView != null) {
-            removeView(mExpandedView);
+        if (!mActivityViewReady) {
+            // release not needed, never initialized?
+            mActivityView = null;
+            return;
         }
-        mExpandedView = view;
-        if (mExpandedView != null) {
-            addView(mExpandedView);
+        // HACK: release() will crash if the view is not attached.
+        if (!isAttachedToWindow()) {
+            mActivityView.setVisibility(View.GONE);
+            tmpParent.addView(this);
         }
+
+        mActivityView.release();
+        ((ViewGroup) getParent()).removeView(this);
+        mActivityView = null;
+        mActivityViewReady = false;
     }
 
-    /**
-     * @return the view containing the expanded content, can be null.
-     */
-    @Nullable
-    public View getExpandedView() {
-        return mExpandedView;
+    private boolean usingActivityView() {
+        return mBubbleIntent != null;
+    }
+
+    private void applyRowState(ExpandableNotificationRow view) {
+        view.reset();
+        view.setHeadsUp(false);
+        view.resetTranslation();
+        view.setOnKeyguard(false);
+        view.setOnAmbient(false);
+        view.setClipBottomAmount(0);
+        view.setClipTopAmount(0);
+        view.setContentTransformationAmount(0, false);
+        view.setIconsVisible(true);
+
+        // TODO - Need to reset this (and others) when view goes back in shade, leave for now
+        // view.setTopRoundness(1, false);
+        // view.setBottomRoundness(1, false);
+
+        ExpandableViewState viewState = view.getViewState();
+        viewState = viewState == null ? new ExpandableViewState() : viewState;
+        viewState.height = view.getIntrinsicHeight();
+        viewState.gone = false;
+        viewState.hidden = false;
+        viewState.dimmed = false;
+        viewState.dark = false;
+        viewState.alpha = 1f;
+        viewState.notGoneIndex = -1;
+        viewState.xTranslation = 0;
+        viewState.yTranslation = 0;
+        viewState.zTranslation = 0;
+        viewState.scaleX = 1;
+        viewState.scaleY = 1;
+        viewState.inShelf = true;
+        viewState.headsUpIsVisible = false;
+        viewState.applyToView(view);
     }
 
     private Intent getSettingsIntent(String packageName, final int appUid) {
@@ -320,6 +521,61 @@
         return intent;
     }
 
+    @Nullable
+    private PendingIntent getBubbleIntent(NotificationEntry entry) {
+        Notification notif = entry.notification.getNotification();
+        String packageName = entry.notification.getPackageName();
+        Notification.BubbleMetadata data = notif.getBubbleMetadata();
+        if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
+                packageName)) {
+            return data.getIntent();
+        } else if (BubbleController.shouldUseContentIntent(mContext)
+                && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
+                packageName)) {
+            return notif.contentIntent;
+        }
+        return null;
+    }
+
+    /**
+     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+     *
+     * @param intent the pending intent of the bubble.
+     * @param enableLogging whether bubble developer error should be logged.
+     * @param packageName the notification package name for this bubble.
+     * @return
+     */
+    private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
+            String packageName) {
+        if (intent == null) {
+            return false;
+        }
+        ActivityInfo info =
+                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+        if (info == null) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+            }
+            return false;
+        }
+        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+            }
+            return false;
+        }
+        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
+            }
+            return false;
+        }
+        return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+    }
+
     /**
      * Listener that is notified when a bubble is blocked.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2b344f6..5546e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,7 +19,6 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import android.app.ActivityView;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -33,13 +32,11 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
@@ -54,8 +51,6 @@
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -63,7 +58,7 @@
 /**
  * Renders bubbles in a stack and handles animating expanded and collapsed states.
  */
-public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleStackView extends FrameLayout {
     private static final String TAG = "BubbleStackView";
 
     /**
@@ -90,27 +85,34 @@
 
     private final SpringAnimation mExpandedViewXAnim;
     private final SpringAnimation mExpandedViewYAnim;
+    private final BubbleData mBubbleData;
 
     private PhysicsAnimationLayout mBubbleContainer;
     private StackAnimationController mStackAnimationController;
     private ExpandedAnimationController mExpandedAnimationController;
 
-    private BubbleExpandedView mExpandedViewContainer;
+    private FrameLayout mExpandedViewContainer;
+
 
     private int mBubbleSize;
     private int mBubblePadding;
     private int mExpandedAnimateXDistance;
     private int mExpandedAnimateYDistance;
+    private int mStatusBarHeight;
+    private int mPipDismissHeight;
 
+    private Bubble mExpandedBubble;
     private boolean mIsExpanded;
-    private int mExpandedBubbleHeight;
+
     private BubbleTouchHandler mTouchHandler;
-    private BubbleView mExpandedBubble;
     private BubbleController.BubbleExpandListener mExpandListener;
+    private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener;
 
     private boolean mViewUpdatedRequested = false;
     private boolean mIsAnimating = false;
 
+    private LayoutInflater mInflater;
+
     // Used for determining view / touch intersection
     int[] mTempLoc = new int[2];
     RectF mTempRect = new RectF();
@@ -140,11 +142,14 @@
         }
     };
 
-    public BubbleStackView(Context context) {
+    public BubbleStackView(Context context, BubbleData data) {
         super(context);
 
-        mTouchHandler = new BubbleTouchHandler(context);
+        mBubbleData = data;
+        mInflater = LayoutInflater.from(context);
+        mTouchHandler = new BubbleTouchHandler(context, this);
         setOnTouchListener(mTouchHandler);
+        mInflater = LayoutInflater.from(context);
 
         Resources res = getResources();
         mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -153,8 +158,11 @@
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
         mExpandedAnimateYDistance =
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mPipDismissHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.pip_dismiss_gradient_height);
 
-        mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
         mDisplaySize = new Point();
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -174,9 +182,7 @@
         mBubbleContainer.setClipChildren(false);
         addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 
-        mExpandedViewContainer = (BubbleExpandedView)
-                LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
-                        this /* parent */, false /* attachToRoot */);
+        mExpandedViewContainer = new FrameLayout(context);
         mExpandedViewContainer.setElevation(elevation);
         mExpandedViewContainer.setPadding(padding, padding, padding, padding);
         mExpandedViewContainer.setClipChildren(false);
@@ -197,6 +203,8 @@
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
 
         setClipChildren(false);
+
+        mBubbleContainer.bringToFront();
     }
 
     @Override
@@ -218,6 +226,17 @@
     }
 
     /**
+     * Updates the visibility of the 'dot' indicating an update on the bubble.
+     * @param key the {@link NotificationEntry#key} associated with the bubble.
+     */
+    public void updateDotVisibility(String key) {
+        Bubble b = mBubbleData.getBubble(key);
+        if (b != null) {
+            b.iconView.updateDotVisibility();
+        }
+    }
+
+    /**
      * Sets the listener to notify when the bubble stack is expanded.
      */
     public void setExpandListener(BubbleController.BubbleExpandListener listener) {
@@ -228,7 +247,10 @@
      * Sets the listener to notify when a bubble is blocked.
      */
     public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
-        mExpandedViewContainer.setOnBlockedListener(listener);
+        mBlockedListener = listener;
+        for (Bubble b : mBubbleData.getBubbles()) {
+            b.expandedView.setOnBlockedListener(mBlockedListener);
+        }
     }
 
     /**
@@ -241,19 +263,29 @@
     /**
      * The {@link BubbleView} that is expanded, null if one does not exist.
      */
-    public BubbleView getExpandedBubble() {
+    BubbleView getExpandedBubbleView() {
+        return mExpandedBubble != null ? mExpandedBubble.iconView : null;
+    }
+
+    /**
+     * The {@link Bubble} that is expanded, null if one does not exist.
+     */
+    Bubble getExpandedBubble() {
         return mExpandedBubble;
     }
 
     /**
      * Sets the bubble that should be expanded and expands if needed.
+     *
+     * @param key the {@link NotificationEntry#key} associated with the bubble to expand.
      */
-    public void setExpandedBubble(BubbleView bubbleToExpand) {
+    void setExpandedBubble(String key) {
+        Bubble bubbleToExpand = mBubbleData.getBubble(key);
         if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) {
             // Previously expanded, notify that this bubble is no longer expanded
-            notifyExpansionChanged(mExpandedBubble, false /* expanded */);
+            notifyExpansionChanged(mExpandedBubble.entry, false /* expanded */);
         }
-        BubbleView prevBubble = mExpandedBubble;
+        Bubble prevBubble = mExpandedBubble;
         mExpandedBubble = bubbleToExpand;
         if (!mIsExpanded) {
             // If we weren't previously expanded we should animate open.
@@ -268,8 +300,8 @@
             logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
         }
-        mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
-        notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+        mExpandedBubble.entry.setShowInShadeWhenBubble(false);
+        notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
     }
 
     /**
@@ -280,7 +312,7 @@
         for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
             if (entry.equals(bv.getEntry())) {
-                setExpandedBubble(bv);
+                setExpandedBubble(entry.key);
             }
         }
     }
@@ -288,48 +320,67 @@
     /**
      * Adds a bubble to the top of the stack.
      *
-     * @param bubbleView the view to add to the stack.
+     * @param entry the notification to add to the stack of bubbles.
      */
-    public void addBubble(BubbleView bubbleView) {
-        mBubbleContainer.addView(bubbleView, 0,
+    public void addBubble(NotificationEntry entry) {
+        Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
+        mBubbleData.addBubble(b);
+
+        mBubbleContainer.addView(b.iconView, 0,
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
+        ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters);
+
         requestUpdate();
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
 
     /**
      * Remove a bubble from the stack.
      */
-    public void removeBubble(BubbleView bubbleView) {
-        int removedIndex = mBubbleContainer.indexOfChild(bubbleView);
-        mBubbleContainer.removeView(bubbleView);
+    public void removeBubble(String key) {
+        Bubble b = mBubbleData.removeBubble(key);
+        if (b == null) {
+            return;
+        }
+        b.entry.setBubbleDismissed(true);
+
+        // Remove it from the views
+        int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
+        b.expandedView.destroyActivityView(this /* tmpParent */);
+        mBubbleContainer.removeView(b.iconView);
+
         int bubbleCount = mBubbleContainer.getChildCount();
         if (bubbleCount == 0) {
             // If no bubbles remain, collapse the entire stack.
             collapseStack();
             return;
-        } else if (bubbleView.equals(mExpandedBubble)) {
+        } else if (b.equals(mExpandedBubble)) {
             // Was the current bubble just removed?
             // If we have other bubbles and are expanded go to the next one or previous
             // if the bubble removed was last
             int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
             BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
             if (mIsExpanded) {
-                setExpandedBubble(expandedBubble);
+                setExpandedBubble(expandedBubble.getKey());
             } else {
                 mExpandedBubble = null;
             }
         }
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
     }
 
     /**
      * Dismiss the stack of bubbles.
      */
     public void stackDismissed() {
+        for (Bubble bubble : mBubbleData.getBubbles()) {
+            bubble.entry.setBubbleDismissed(true);
+            bubble.expandedView.destroyActivityView(this /* tmpParent */);
+        }
+        mBubbleData.clear();
         collapseStack();
         mBubbleContainer.removeAllViews();
+        mExpandedViewContainer.removeAllViews();
         logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
                 StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
     }
@@ -337,25 +388,25 @@
     /**
      * Updates a bubble in the stack.
      *
-     * @param bubbleView the view to update in the stack.
-     * @param entry the entry to update it with.
+     * @param entry the entry to update in the stack.
      * @param updatePosition whether this bubble should be moved to top of the stack.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationEntry entry,
-            boolean updatePosition) {
-        bubbleView.update(entry);
+    public void updateBubble(NotificationEntry entry, boolean updatePosition) {
+        Bubble b = mBubbleData.getBubble(entry.key);
+        mBubbleData.updateBubble(entry.key, entry);
+
         if (updatePosition && !mIsExpanded) {
             // If alerting it gets promoted to top of the stack.
-            if (mBubbleContainer.indexOfChild(bubbleView) != 0) {
-                mBubbleContainer.moveViewTo(bubbleView, 0);
+            if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
+                mBubbleContainer.moveViewTo(b.iconView, 0);
             }
             requestUpdate();
         }
-        if (mIsExpanded && bubbleView.equals(mExpandedBubble)) {
+        if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
             entry.setShowInShadeWhenBubble(false);
             requestUpdate();
         }
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
     }
 
     /**
@@ -393,7 +444,7 @@
         if (mIsExpanded) {
             // TODO: Save opened bubble & move it to top of stack
             animateExpansion(false /* shouldExpand */);
-            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+            notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         }
     }
@@ -412,8 +463,8 @@
     @MainThread
     public void expandStack() {
         if (!mIsExpanded) {
-            mExpandedBubble = getTopBubble();
-            setExpandedBubble(mExpandedBubble);
+            String expandedBubbleKey = getBubbleAt(0).getKey();
+            setExpandedBubble(expandedBubbleKey);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
         }
     }
@@ -438,9 +489,10 @@
             if (shouldExpand) {
                 mBubbleContainer.setController(mExpandedAnimationController);
                 mExpandedAnimationController.expandFromStack(
-                                mStackAnimationController.getStackPosition(), () -> {
-                                updatePointerPosition();
-                                updateAfter.run();
+                        mStackAnimationController.getStackPosition(),
+                        () -> {
+                            updatePointerPosition();
+                            updateAfter.run();
                         });
             } else {
                 mBubbleContainer.cancelAllAnimations();
@@ -459,7 +511,8 @@
             final float yStart = Math.min(
                     mStackAnimationController.getStackPosition().y,
                     mExpandedAnimateYDistance);
-            final float yDest = getStatusBarHeight() + mExpandedBubble.getHeight() + mBubblePadding;
+            final float yDest = getStatusBarHeight()
+                    + mExpandedBubble.iconView.getHeight() + mBubblePadding;
 
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
@@ -484,17 +537,12 @@
                 + mBubbleContainer.getPaddingStart();
     }
 
-    private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) {
+    private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) {
         if (mExpandListener != null) {
-            NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null;
             mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
         }
     }
 
-    private BubbleView getTopBubble() {
-        return getBubbleAt(0);
-    }
-
     /** Return the BubbleView at the given index from the bubble container. */
     public BubbleView getBubbleAt(int i) {
         return mBubbleContainer.getChildCount() > i
@@ -502,55 +550,49 @@
                 : null;
     }
 
-    @Override
-    public void setPosition(float x, float y) {
-        mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public PointF getPosition() {
+    public PointF getStackPosition() {
         return mStackAnimationController.getStackPosition();
     }
 
     /** Called when a drag operation on an individual bubble has started. */
-    public void onBubbleDragStart(BubbleView bubble) {
-        // TODO: Save position and snap back if not dismissed.
+    public void onBubbleDragStart(View bubble) {
+        mExpandedAnimationController.prepareForBubbleDrag(bubble);
     }
 
     /** Called with the coordinates to which an individual bubble has been dragged. */
-    public void onBubbleDragged(BubbleView bubble, float x, float y) {
-        bubble.setTranslationX(x);
-        bubble.setTranslationY(y);
+    public void onBubbleDragged(View bubble, float x, float y) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        mExpandedAnimationController.dragBubbleOut(bubble, x, y);
     }
 
     /** Called when a drag operation on an individual bubble has finished. */
-    public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) {
-        // TODO: Add fling to bottom to dismiss.
+    public void onBubbleDragFinish(
+            View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        if (dismissed) {
+            mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY);
+        } else {
+            mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
+        }
     }
 
     void onDragStart() {
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
         mStackAnimationController.cancelStackPositionAnimations();
         mBubbleContainer.setController(mStackAnimationController);
-        mIsAnimating = false;
     }
 
     void onDragged(float x, float y) {
-        // TODO: We can drag if animating - just need to reroute inflight anims to drag point.
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
@@ -613,6 +655,20 @@
     }
 
     /**
+     * Calculates how large the expanded view of the bubble can be. This takes into account the
+     * y position when the bubbles are expanded as well as the bounds of the dismiss target.
+     */
+    int getMaxExpandedHeight() {
+        int expandedY = (int) mExpandedAnimationController.getExpandedY();
+        int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null
+                ? mBubbleContainer.getChildAt(0).getHeight()
+                : 0;
+        // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
+        int pipDismissHeight = mPipDismissHeight - getBottomInset();
+        return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
+    }
+
+    /**
      * Minimum velocity, in pixels/second, required to get from x to destX while being slowed by a
      * given frictional force.
      *
@@ -639,7 +695,7 @@
         if (getRootWindowInsets() != null) {
             WindowInsets insets = getRootWindowInsets();
             return Math.max(
-                    insets.getSystemWindowInsetTop(),
+                    mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
@@ -648,6 +704,14 @@
         return 0;
     }
 
+    private int getBottomInset() {
+        if (getRootWindowInsets() != null) {
+            WindowInsets insets = getRootWindowInsets();
+            return insets.getSystemWindowInsetBottom();
+        }
+        return 0;
+    }
+
     private boolean isIntersecting(View view, float x, float y) {
         mTempLoc = view.getLocationOnScreen();
         mTempRect.set(mTempLoc[0], mTempLoc[1], mTempLoc[0] + view.getWidth(),
@@ -665,31 +729,11 @@
     }
 
     private void updateExpandedBubble() {
-        if (mExpandedBubble == null) {
-            return;
-        }
-
-        mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this);
-        if (mExpandedBubble.hasAppOverlayIntent()) {
-            // Bubble with activity view expanded state
-            ActivityView expandedView = mExpandedBubble.getActivityView();
-            // XXX: gets added to linear layout
-            expandedView.setLayoutParams(new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
-
-            mExpandedViewContainer.setExpandedView(expandedView);
-        } else {
-            // Bubble with notification view expanded state
-            ExpandableNotificationRow row = mExpandedBubble.getRowView();
-            if (row.getParent() != null) {
-                // Row might still be in the shade when we expand
-                ((ViewGroup) row.getParent()).removeView(row);
-            }
-            if (mIsExpanded) {
-                mExpandedViewContainer.setExpandedView(row);
-            } else {
-                mExpandedViewContainer.setExpandedView(null);
-            }
+        mExpandedViewContainer.removeAllViews();
+        if (mExpandedBubble != null && mIsExpanded) {
+            mExpandedViewContainer.addView(mExpandedBubble.expandedView);
+            mExpandedBubble.expandedView.populateActivityView();
+            mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         }
     }
 
@@ -697,18 +741,10 @@
         Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
 
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
-        if (!mIsExpanded) {
-            mExpandedViewContainer.setExpandedView(null);
-        } else {
-            View expandedView = mExpandedViewContainer.getExpandedView();
-            if (expandedView instanceof ActivityView) {
-                if (expandedView.isAttachedToWindow()) {
-                    ((ActivityView) expandedView).onLocationChanged();
-                }
-            } else {
-                applyRowState(mExpandedBubble.getRowView());
-            }
+        if (mIsExpanded) {
+            mExpandedBubble.expandedView.updateView();
         }
+
         int bubbsCount = mBubbleContainer.getChildCount();
         for (int i = 0; i < bubbsCount; i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
@@ -730,46 +766,12 @@
 
     private void updatePointerPosition() {
         if (mExpandedBubble != null) {
-            float pointerPosition = mExpandedBubble.getPosition().x
-                    + (mExpandedBubble.getWidth() / 2f);
-            mExpandedViewContainer.setPointerPosition((int) pointerPosition);
+            float pointerPosition = mExpandedBubble.iconView.getTranslationX()
+                    + (mExpandedBubble.iconView.getWidth() / 2f);
+            mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
         }
     }
 
-    private void applyRowState(ExpandableNotificationRow view) {
-        view.reset();
-        view.setHeadsUp(false);
-        view.resetTranslation();
-        view.setOnKeyguard(false);
-        view.setOnAmbient(false);
-        view.setClipBottomAmount(0);
-        view.setClipTopAmount(0);
-        view.setContentTransformationAmount(0, false);
-        view.setIconsVisible(true);
-
-        // TODO - Need to reset this (and others) when view goes back in shade, leave for now
-        // view.setTopRoundness(1, false);
-        // view.setBottomRoundness(1, false);
-
-        ExpandableViewState viewState = view.getViewState();
-        viewState = viewState == null ? new ExpandableViewState() : viewState;
-        viewState.height = view.getIntrinsicHeight();
-        viewState.gone = false;
-        viewState.hidden = false;
-        viewState.dimmed = false;
-        viewState.dark = false;
-        viewState.alpha = 1f;
-        viewState.notGoneIndex = -1;
-        viewState.xTranslation = 0;
-        viewState.yTranslation = 0;
-        viewState.zTranslation = 0;
-        viewState.scaleX = 1;
-        viewState.scaleY = 1;
-        viewState.inShelf = true;
-        viewState.headsUpIsVisible = false;
-        viewState.applyToView(view);
-    }
-
     /**
      * @return the number of bubbles in the stack view.
      */
@@ -780,19 +782,19 @@
     /**
      * Finds the bubble index within the stack.
      *
-     * @param bubbleView the view of the bubble.
+     * @param bubble the bubble to look up.
      * @return the index of the bubble view within the bubble stack. The range of the position
      * is between 0 and the bubble count minus 1.
      */
-    public int getBubbleIndex(BubbleView bubbleView) {
-        return mBubbleContainer.indexOfChild(bubbleView);
+    int getBubbleIndex(Bubble bubble) {
+        return mBubbleContainer.indexOfChild(bubble.iconView);
     }
 
     /**
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedXPosition() {
-        return new BigDecimal(getPosition().x / mDisplaySize.x)
+        return new BigDecimal(getStackPosition().x / mDisplaySize.x)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -801,7 +803,7 @@
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedYPosition() {
-        return new BigDecimal(getPosition().y / mDisplaySize.y)
+        return new BigDecimal(getStackPosition().y / mDisplaySize.y)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -813,7 +815,7 @@
      *               the user interaction is not specific to one bubble.
      * @param action the user interaction enum.
      */
-    private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+    private void logBubbleEvent(@Nullable Bubble bubble, int action) {
         if (bubble == null) {
             StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
                     null /* package name */,
@@ -825,7 +827,7 @@
                     getNormalizedXPosition(),
                     getNormalizedYPosition());
         } else {
-            StatusBarNotification notification = bubble.getEntry().notification;
+            StatusBarNotification notification = bubble.entry.notification;
             StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
                     notification.getPackageName(),
                     notification.getNotification().getChannelId(),
@@ -837,4 +839,15 @@
                     getNormalizedYPosition());
         }
     }
+
+    /**
+     * Called when a back gesture should be directed to the Bubbles stack. When expanded,
+     * a back key down/up event pair is forwarded to the bubble Activity.
+     */
+    boolean performBackPressIfNeeded() {
+        if (!isExpanded()) {
+            return false;
+        }
+        return mExpandedBubble.expandedView.performBackPressIfNeeded();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 22cd2fc..165eb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -34,17 +34,16 @@
  * dismissing, and flings.
  */
 class BubbleTouchHandler implements View.OnTouchListener {
+    /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
+    private static final float DISMISS_MIN_VELOCITY = 4000f;
+
+    private final PointF mTouchDown = new PointF();
+    private final PointF mViewPositionOnTouchDown = new PointF();
+    private final BubbleStackView mStack;
 
     private BubbleController mController = Dependency.get(BubbleController.class);
     private PipDismissViewController mDismissViewController;
 
-    // The position of the bubble on down event
-    private float mBubbleDownPosX;
-    private float mBubbleDownPosY;
-    // The touch position on down event
-    private float mDownX = -1;
-    private float mDownY = -1;
-
     private boolean mMovedEnough;
     private int mTouchSlopSquared;
     private VelocityTracker mVelocityTracker;
@@ -58,65 +57,42 @@
         }
     };
 
-    // Bubble being dragged from the row of bubbles when the stack is expanded
-    private BubbleView mBubbleDraggingOut;
+    /** View that was initially touched, when we received the first ACTION_DOWN event. */
+    private View mTouchedView;
 
-    /**
-     * Views movable by this touch handler should implement this interface.
-     */
-    public interface FloatingView {
-
-        /**
-         * Sets the position of the view.
-         */
-        void setPosition(float x, float y);
-
-        /**
-         * Sets the x position of the view.
-         */
-        void setPositionX(float x);
-
-        /**
-         * Sets the y position of the view.
-         */
-        void setPositionY(float y);
-
-        /**
-         * @return the position of the view.
-         */
-        PointF getPosition();
-    }
-
-    public BubbleTouchHandler(Context context) {
+    BubbleTouchHandler(Context context, BubbleStackView stackView) {
         final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mTouchSlopSquared = touchSlop * touchSlop;
         mDismissViewController = new PipDismissViewController(context);
+        mStack = stackView;
     }
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        int action = event.getActionMasked();
+        final int action = event.getActionMasked();
 
-        BubbleStackView stack = (BubbleStackView) v;
-        View targetView = mBubbleDraggingOut != null
-                ? mBubbleDraggingOut
-                : stack.getTargetView(event);
-        boolean isFloating = targetView instanceof FloatingView;
-        if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
-            stack.collapseStack();
+        // If we aren't currently in the process of touching a view, figure out what we're touching.
+        // It'll be the stack, an individual bubble, or nothing.
+        if (mTouchedView == null) {
+            mTouchedView = mStack.getTargetView(event);
+        }
+
+        // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
+        // anything, collapse the stack.
+        if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
+            mStack.collapseStack();
             cleanUpDismissTarget();
-            resetTouches();
+            mTouchedView = null;
             return false;
         }
 
-        FloatingView floatingView = (FloatingView) targetView;
-        boolean isBubbleStack = floatingView instanceof BubbleStackView;
+        final boolean isStack = mStack.equals(mTouchedView);
+        final float rawX = event.getRawX();
+        final float rawY = event.getRawY();
 
-        PointF startPos = floatingView.getPosition();
-        float rawX = event.getRawX();
-        float rawY = event.getRawY();
-        float x = mBubbleDownPosX + rawX - mDownX;
-        float y = mBubbleDownPosY + rawY - mDownY;
+        // The coordinates of the touch event, in terms of the touched view's position.
+        final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
+        final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 trackMovement(event);
@@ -124,87 +100,83 @@
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
 
-                mBubbleDownPosX = startPos.x;
-                mBubbleDownPosY = startPos.y;
-                mDownX = rawX;
-                mDownY = rawY;
-                mMovedEnough = false;
+                mTouchDown.set(rawX, rawY);
 
-                if (isBubbleStack) {
-                    stack.onDragStart();
+                if (isStack) {
+                    mViewPositionOnTouchDown.set(mStack.getStackPosition());
+                    mStack.onDragStart();
                 } else {
-                    stack.onBubbleDragStart((BubbleView) floatingView);
+                    mViewPositionOnTouchDown.set(
+                            mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
+                    mStack.onBubbleDragStart(mTouchedView);
                 }
 
                 break;
-
             case MotionEvent.ACTION_MOVE:
                 trackMovement(event);
+                final float deltaX = rawX - mTouchDown.x;
+                final float deltaY = rawY - mTouchDown.y;
 
-                if (mBubbleDownPosX == -1 || mDownX == -1) {
-                    mBubbleDownPosX = startPos.x;
-                    mBubbleDownPosY = startPos.y;
-                    mDownX = rawX;
-                    mDownY = rawY;
-                }
-                final float deltaX = rawX - mDownX;
-                final float deltaY = rawY - mDownY;
                 if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
                     mMovedEnough = true;
                 }
 
                 if (mMovedEnough) {
-                    if (floatingView instanceof BubbleView) {
-                        mBubbleDraggingOut = ((BubbleView) floatingView);
-                        stack.onBubbleDragged(mBubbleDraggingOut, x, y);
+                    if (isStack) {
+                        mStack.onDragged(viewX, viewY);
                     } else {
-                        stack.onDragged(x, y);
+                        mStack.onBubbleDragged(mTouchedView, viewX, viewY);
                     }
                 }
+
                 // TODO - when we're in the target stick to it / animate in some way?
                 mInDismissTarget = mDismissViewController.updateTarget(
-                        isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView);
+                        isStack ? mStack.getBubbleAt(0) : mTouchedView);
                 break;
 
             case MotionEvent.ACTION_CANCEL:
-                resetTouches();
+                mTouchedView = null;
                 cleanUpDismissTarget();
                 break;
 
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mInDismissTarget) {
-                    if (isBubbleStack) {
-                        mController.dismissStack();
-                    } else {
-                        mController.removeBubble(((BubbleView) floatingView).getKey());
-                    }
+                if (mInDismissTarget && isStack) {
+                    mController.dismissStack();
                 } else if (mMovedEnough) {
                     mVelocityTracker.computeCurrentVelocity(1000);
                     final float velX = mVelocityTracker.getXVelocity();
                     final float velY = mVelocityTracker.getYVelocity();
-                    if (isBubbleStack) {
-                        stack.onDragFinish(x, y, velX, velY);
+                    if (isStack) {
+                        mStack.onDragFinish(viewX, viewY, velX, velY);
                     } else {
-                        stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY);
+                        final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY;
+                        mStack.onBubbleDragFinish(
+                                mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed);
+                        if (dismissed) {
+                            mController.removeBubble(((BubbleView) mTouchedView).getKey());
+                        }
                     }
-                } else if (floatingView.equals(stack.getExpandedBubble())) {
-                    stack.collapseStack();
-                } else if (isBubbleStack) {
-                    if (stack.isExpanded()) {
-                        stack.collapseStack();
+                } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) {
+                    mStack.collapseStack();
+                } else if (isStack) {
+                    if (mStack.isExpanded()) {
+                        mStack.collapseStack();
                     } else {
-                        stack.expandStack();
+                        mStack.expandStack();
                     }
                 } else {
-                    stack.setExpandedBubble((BubbleView) floatingView);
+                    mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
                 }
+
                 cleanUpDismissTarget();
                 mVelocityTracker.recycle();
                 mVelocityTracker = null;
-                resetTouches();
+                mTouchedView = null;
+                mMovedEnough = false;
                 break;
         }
+
         return true;
     }
 
@@ -216,16 +188,6 @@
         mDismissViewController.destroyDismissTarget();
     }
 
-    /**
-     * Resets anything we care about after a gesture is complete.
-     */
-    private void resetTouches() {
-        mDownX = -1;
-        mDownY = -1;
-        mBubbleDownPosX = -1;
-        mBubbleDownPosY = -1;
-        mBubbleDraggingOut = null;
-    }
 
     private void trackMovement(MotionEvent event) {
         if (mVelocityTracker == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 74ddc8f..b409a31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -17,30 +17,19 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.Nullable;
-import android.app.ActivityView;
 import android.app.Notification;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -49,7 +38,7 @@
 /**
  * A floating object on the screen that can post message updates.
  */
-public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout {
     private static final String TAG = "BubbleView";
 
     // Same value as Launcher3 badge code
@@ -62,11 +51,6 @@
     private int mIconInset;
 
     private NotificationEntry mEntry;
-    private PendingIntent mAppOverlayIntent;
-    private BubbleController mBubbleController;
-    private ActivityView mActivityView;
-    private boolean mActivityViewReady;
-    private boolean mActivityViewStarted;
 
     public BubbleView(Context context) {
         this(context, null);
@@ -86,7 +70,6 @@
         // XXX: can this padding just be on the view and we look it up?
         mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
         mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
-        mBubbleController = Dependency.get(BubbleController.class);
     }
 
     @Override
@@ -168,7 +151,6 @@
         updateViews();
     }
 
-
     /**
      * @return the {@link ExpandableNotificationRow} view to display notification content when the
      * bubble is expanded.
@@ -234,123 +216,4 @@
         // XXX: should we pull from the drawable, app icon, notif tint?
         return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
     }
-
-    /**
-     * @return a view used to display app overlay content when expanded.
-     */
-    public ActivityView getActivityView() {
-        if (mActivityView == null) {
-            mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
-                    true /* singleTaskInstance */);
-            Log.d(TAG, "[getActivityView] created: " + mActivityView);
-            mActivityView.setCallback(new ActivityView.StateCallback() {
-                @Override
-                public void onActivityViewReady(ActivityView view) {
-                    mActivityViewReady = true;
-                    mActivityView.startActivity(mAppOverlayIntent);
-                }
-
-                @Override
-                public void onActivityViewDestroyed(ActivityView view) {
-                    mActivityViewReady = false;
-                }
-
-                /**
-                 * This is only called for tasks on this ActivityView, which is also set to
-                 * single-task mode -- meaning never more than one task on this display. If a task
-                 * is being removed, it's the top Activity finishing and this bubble should
-                 * be removed or collapsed.
-                 */
-                @Override
-                public void onTaskRemovalStarted(int taskId) {
-                    if (mEntry != null) {
-                        // Must post because this is called from a binder thread.
-                        post(() -> mBubbleController.removeBubble(mEntry.key));
-                    }
-                }
-            });
-            mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
-                ActivityView activityView = (ActivityView) view;
-                // Here we assume that the position of the ActivityView on the screen
-                // remains regardless of IME status. When we move ActivityView, the
-                // forwardedInsets should be computed not against the current location
-                // and size, but against the post-moved location and size.
-                Point displaySize = new Point();
-                view.getContext().getDisplay().getSize(displaySize);
-                int[] windowLocation = view.getLocationOnScreen();
-                final int windowBottom = windowLocation[1] + view.getHeight();
-                final int keyboardHeight = insets.getSystemWindowInsetBottom()
-                        - insets.getStableInsetBottom();
-                final int insetsBottom = Math.max(0,
-                        windowBottom + keyboardHeight - displaySize.y);
-                activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
-                return view.onApplyWindowInsets(insets);
-            });
-
-        }
-        return mActivityView;
-    }
-
-    /**
-     * Removes and releases an ActivityView if one was previously created for this bubble.
-     */
-    public void destroyActivityView(ViewGroup tmpParent) {
-        if (mActivityView == null) {
-            return;
-        }
-        if (!mActivityViewReady) {
-            // release not needed, never initialized?
-            mActivityView = null;
-            return;
-        }
-        // HACK: release() will crash if the view is not attached.
-        if (!mActivityView.isAttachedToWindow()) {
-            mActivityView.setVisibility(View.GONE);
-            tmpParent.addView(mActivityView, new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT));
-        }
-
-        mActivityView.release();
-
-        ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
-        mActivityView = null;
-    }
-
-    @Override
-    public void setPosition(float x, float y) {
-        setPositionX(x);
-        setPositionY(y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        setTranslationX(x);
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        setTranslationY(y);
-    }
-
-    @Override
-    public PointF getPosition() {
-        return new PointF(getTranslationX(), getTranslationY());
-    }
-
-    /**
-     * @return whether an ActivityView should be used to display the content of this Bubble
-     */
-    public boolean hasAppOverlayIntent() {
-        return mAppOverlayIntent != null;
-    }
-
-    public PendingIntent getAppOverlayIntent() {
-        return mAppOverlayIntent;
-
-    }
-
-    public void setBubbleIntent(PendingIntent intent) {
-        mAppOverlayIntent = intent;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 1644064..f0d9be1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles.animation;
 
+import android.content.res.Resources;
 import android.graphics.PointF;
 import android.view.View;
 import android.view.WindowInsets;
@@ -43,6 +44,9 @@
      */
     private static final int ANIMATE_TRANSLATION_FACTOR = 4;
 
+    /** How much to scale down bubbles when they're animating in/out. */
+    private static final float ANIMATE_SCALE_PERCENT = 0.5f;
+
     /**
      * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack}
      * and used to return to stack form in {@link #collapseBackToStack}.
@@ -55,16 +59,35 @@
     private float mBubblePaddingPx;
     /** Size of each bubble. */
     private float mBubbleSizePx;
+    /** Height of the status bar. */
+    private float mStatusBarHeight;
+
+    /**
+     * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+     * the rest of the bubbles to animate to fill the gap.
+     */
+    private boolean mBubbleDraggedOutEnough = false;
+
+    /** The bubble currently being dragged out of the row (to potentially be dismissed). */
+    private View mBubbleDraggingOut;
+
+    /**
+     * Drag velocities for the dragging-out bubble when the drag finished. These are used by
+     * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity.
+     */
+    private float mBubbleDraggingOutVelX;
+    private float mBubbleDraggingOutVelY;
 
     @Override
     protected void setLayout(PhysicsAnimationLayout layout) {
         super.setLayout(layout);
-        mStackOffsetPx = layout.getResources().getDimensionPixelSize(
-                R.dimen.bubble_stack_offset);
-        mBubblePaddingPx = layout.getResources().getDimensionPixelSize(
-                R.dimen.bubble_padding);
-        mBubbleSizePx = layout.getResources().getDimensionPixelSize(
-                R.dimen.individual_bubble_size);
+
+        final Resources res = layout.getResources();
+        mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
+        mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
+        mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
@@ -98,12 +121,93 @@
         runAfterTranslationsEnd(after);
     }
 
+    /** Prepares the given bubble to be dragged out. */
+    public void prepareForBubbleDrag(View bubble) {
+        mLayout.cancelAnimationsOnView(bubble);
+
+        mBubbleDraggingOut = bubble;
+        mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE);
+    }
+
+    /**
+     * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to
+     * take its place once it's dragged out of the row of bubbles, and animate out of the way if the
+     * bubble is dragged back into the row.
+     */
+    public void dragBubbleOut(View bubbleView, float x, float y) {
+        bubbleView.setTranslationX(x);
+        bubbleView.setTranslationY(y);
+
+        final boolean draggedOutEnough =
+                y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+        if (draggedOutEnough != mBubbleDraggedOutEnough) {
+            animateStackByBubbleWidthsStartingFrom(
+                    /* numBubbleWidths */ draggedOutEnough ? -1 : 0,
+                    /* startIndex */ mLayout.indexOfChild(bubbleView) + 1);
+            mBubbleDraggedOutEnough = draggedOutEnough;
+        }
+    }
+
+    /**
+     * Snaps a bubble back to its position within the bubble row, and animates the rest of the
+     * bubbles to accommodate it if it was previously dragged out past the threshold.
+     */
+    public void snapBubbleBack(View bubbleView, float velX, float velY) {
+        final int index = mLayout.indexOfChild(bubbleView);
+
+        // Snap the bubble back, respecting its current velocity.
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX);
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY);
+        mLayout.setEndListenerForProperties(
+                mLayout.new OneTimeMultiplePropertyEndListener() {
+                    @Override
+                    void onAllAnimationsForPropertiesEnd() {
+                        // Reset Z translation once the bubble is done snapping back.
+                        bubbleView.setTranslationZ(0f);
+                    }
+                },
+                DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        animateStackByBubbleWidthsStartingFrom(
+                /* numBubbleWidths */ 0, /* startIndex */ index + 1);
+
+        mBubbleDraggingOut = null;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Sets configuration variables so that when the given bubble is removed, the animations are
+     * started with the given velocities.
+     */
+    public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) {
+        mBubbleDraggingOut = bubbleView;
+        mBubbleDraggingOutVelX = velX;
+        mBubbleDraggingOutVelY = velY;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Animates the bubbles, starting at the given index, to the left or right by the given number
+     * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal
+     * positions.
+     */
+    private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) {
+        for (int i = startIndex; i < mLayout.getChildCount(); i++) {
+            mLayout.animateValueForChildAtIndex(
+                    DynamicAnimation.TRANSLATION_X,
+                    i,
+                    getXForChildAtIndex(i + numBubbleWidths));
+        }
+    }
+
     /** The Y value of the row of expanded bubbles. */
-    private float getExpandedY() {
-        final WindowInsets insets = mLayout.getRootWindowInsets();
+    public float getExpandedY() {
+        final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
         if (insets != null) {
             return mBubblePaddingPx + Math.max(
-                    insets.getSystemWindowInsetTop(),
+                    mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
@@ -161,12 +265,7 @@
         child.setTranslationX(getXForChildAtIndex(index));
         child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY());
-
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index + 1; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
-        }
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -175,16 +274,36 @@
         // TODO: Reverse this when bubbles are at the bottom.
         mLayout.animateValueForChild(
                 DynamicAnimation.ALPHA, child, 0f, finishRemoval);
-        mLayout.animateValueForChild(
-                DynamicAnimation.TRANSLATION_Y,
-                child,
-                getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
 
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
+        // If we're removing the dragged-out bubble, that means it got dismissed.
+        if (child.equals(mBubbleDraggingOut)) {
+            // Throw it to the bottom of the screen, towards the center horizontally.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_X,
+                    child,
+                    mLayout.getWidth() / 2f - mBubbleSizePx / 2f,
+                    mBubbleDraggingOutVelX);
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    mLayout.getHeight() + mBubbleSizePx,
+                    mBubbleDraggingOutVelY);
+
+            // Scale it down a bit so it looks like it's disappearing.
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT);
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT);
+
+            mBubbleDraggingOut = null;
+        } else {
+            // If we're removing some random bubble just throw it off the top.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         }
+
+        // Animate all the other bubbles to their new positions sans this bubble.
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -203,6 +322,23 @@
                 () -> super.setChildVisibility(child, index, visibility));
     }
 
+    /**
+     * Animates the bubbles after the given index to the X position they should be in according to
+     * {@link #getXForChildAtIndex}.
+     */
+    private void animateBubblesAfterIndexToCorrectX(int start) {
+        for (int i = start; i < mLayout.getChildCount(); i++) {
+            final View bubble = mLayout.getChildAt(i);
+
+            // Don't animate the dragging out bubble, or it'll jump around while being dragged. It
+            // will be snapped to the correct X value after the drag (if it's not dismissed).
+            if (!bubble.equals(mBubbleDraggingOut)) {
+                mLayout.animateValueForChild(
+                        DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i));
+            }
+        }
+    }
+
     /** Returns the appropriate X translation value for a bubble at the given index. */
     private float getXForChildAtIndex(int index) {
         return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index a4ddbf7..dfdcfc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -187,6 +187,20 @@
     }
 
     /**
+     * Sets an end listener that will be called whenever any of the given properties' animations
+     * end. For example, setting a listener for TRANSLATION_X and TRANSLATION_Y will result in that
+     * listener being called twice - once when all TRANSLATION_X animations end, and again when all
+     * TRANSLATION_Y animations end.
+     */
+    public void setEndListenerForProperties(
+            DynamicAnimation.OnAnimationEndListener endListener,
+            DynamicAnimation.ViewProperty... properties) {
+        for (DynamicAnimation.ViewProperty property : properties) {
+            setEndListenerForProperty(endListener, property);
+        }
+    }
+
+    /**
      * Removes the end listener that would have been called when all child animations for a given
      * property stopped running.
      */
@@ -197,7 +211,6 @@
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         super.addView(child, index, params);
-        setChildrenVisibility();
 
         // Set up animations for the new view, if the controller is set. If it isn't set, we'll be
         // setting up animations for all children when setController is called.
@@ -208,6 +221,8 @@
 
             mController.onChildAdded(child, index);
         }
+
+        setChildrenVisibility();
     }
 
     @Override
@@ -294,6 +309,13 @@
         }
     }
 
+    /** Cancels all of the physics animations running on the given view. */
+    public void cancelAnimationsOnView(View view) {
+        for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
+            getAnimationFromView(property, view).cancel();
+        }
+    }
+
     /**
      * Animates the property of the given child view, then runs the callback provided when the
      * animation ends.
@@ -318,6 +340,11 @@
                 });
             }
 
+            // Set the start velocity if it's something other than the not-set value.
+            if (startVel != Float.MAX_VALUE) {
+                animation.setStartVelocity(startVel);
+            }
+
             animation.animateToFinalPosition(value);
         }
     }
@@ -337,6 +364,14 @@
         animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null);
     }
 
+    protected void animateValueForChild(
+            DynamicAnimation.ViewProperty property,
+            View view,
+            float value,
+            float startVel) {
+        animateValueForChild(property, view, value, startVel, /* after */ null);
+    }
+
     /**
      * Animates the property of the child at the given index to the given value, then runs the
      * callback provided when the animation ends.
@@ -414,7 +449,13 @@
      */
     private SpringAnimation getAnimationAtIndex(
             DynamicAnimation.ViewProperty property, int index) {
-        return (SpringAnimation) getChildAt(index).getTag(getTagIdForProperty(property));
+        return getAnimationFromView(property, getChildAt(index));
+    }
+
+    /** Retrieves the animation of the given property from the view via the view tag system. */
+    private SpringAnimation getAnimationFromView(
+            DynamicAnimation.ViewProperty property, View view) {
+        return (SpringAnimation) view.getTag(getTagIdForProperty(property));
     }
 
     /** Sets up SpringAnimations of the given property for each child view in the layout. */
@@ -528,4 +569,33 @@
             }
         }
     }
+
+    /**
+     * One time end listener that waits for every animation on every given property to finish. At
+     * that point, it calls {@link #onAllAnimationsForPropertiesEnd} and then removes itself as an
+     * end listener from each property.
+     */
+    public abstract class OneTimeMultiplePropertyEndListener
+            implements DynamicAnimation.OnAnimationEndListener {
+        final DynamicAnimation.ViewProperty[] mViewProperties;
+
+        OneTimeMultiplePropertyEndListener(DynamicAnimation.ViewProperty... properties) {
+            mViewProperties = properties;
+        }
+
+        @Override
+        public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+                float velocity) {
+            if (!arePropertiesAnimating(mViewProperties)) {
+                onAllAnimationsForPropertiesEnd();
+
+                for (DynamicAnimation.ViewProperty property : mViewProperties) {
+                    removeEndListenerForProperty(property);
+                }
+            }
+        }
+
+        /** Called when every animation for every property has finished. */
+        abstract void onAllAnimationsForPropertiesEnd();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 0c089a75..7dfb21c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui.bubbles.animation;
 
-import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.FlingAnimation;
@@ -88,9 +85,8 @@
     private int mBubbleOffscreen;
     /** How far down the screen the stack starts, when there is no pre-existing location. */
     private int mStackStartingVerticalOffset;
-
-    private Point mDisplaySize;
-    private RectF mAllowableStackPositionRegion;
+    /** Height of the status bar. */
+    private float mStatusBarHeight;
 
     @Override
     protected void setLayout(PhysicsAnimationLayout layout) {
@@ -103,11 +99,8 @@
         mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
         mStackStartingVerticalOffset =
                 res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
-
-        mDisplaySize = new Point();
-        WindowManager wm =
-                (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getSize(mDisplaySize);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
@@ -203,10 +196,9 @@
      */
     public RectF getAllowableStackPositionRegion() {
         final WindowInsets insets = mLayout.getRootWindowInsets();
-        mAllowableStackPositionRegion = new RectF();
-
+        final RectF allowableRegion = new RectF();
         if (insets != null) {
-            mAllowableStackPositionRegion.left =
+            allowableRegion.left =
                     -mBubbleOffscreen
                             - mBubblePadding
                             + Math.max(
@@ -214,7 +206,7 @@
                             insets.getDisplayCutout() != null
                                     ? insets.getDisplayCutout().getSafeInsetLeft()
                                     : 0);
-            mAllowableStackPositionRegion.right =
+            allowableRegion.right =
                     mLayout.getWidth()
                             - mIndividualBubbleSize
                             + mBubbleOffscreen
@@ -222,17 +214,17 @@
                             - Math.max(
                             insets.getSystemWindowInsetRight(),
                             insets.getDisplayCutout() != null
-                                ? insets.getDisplayCutout().getSafeInsetRight()
-                                : 0);
+                                    ? insets.getDisplayCutout().getSafeInsetRight()
+                                    : 0);
 
-            mAllowableStackPositionRegion.top =
+            allowableRegion.top =
                     mBubblePadding
                             + Math.max(
-                            insets.getSystemWindowInsetTop(),
+                            mStatusBarHeight,
                             insets.getDisplayCutout() != null
-                                ? insets.getDisplayCutout().getSafeInsetTop()
-                                : 0);
-            mAllowableStackPositionRegion.bottom =
+                                    ? insets.getDisplayCutout().getSafeInsetTop()
+                                    : 0);
+            allowableRegion.bottom =
                     mLayout.getHeight()
                             - mIndividualBubbleSize
                             - mBubblePadding
@@ -243,7 +235,7 @@
                                     : 0);
         }
 
-        return mAllowableStackPositionRegion;
+        return allowableRegion;
     }
 
     @Override
@@ -287,31 +279,14 @@
 
     @Override
     void onChildAdded(View child, int index) {
-        // If this is the first child added, position the stack in its starting position.
         if (mLayout.getChildCount() == 1) {
-            moveStackToStartPosition();
-        }
-
-        if (mLayout.indexOfChild(child) == 0) {
-            child.setTranslationY(mStackPosition.y);
-
-            // Pop in the new bubble.
-            child.setScaleX(ANIMATE_IN_STARTING_SCALE);
-            child.setScaleY(ANIMATE_IN_STARTING_SCALE);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
-
-            // Fade in the new bubble.
-            child.setAlpha(0);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
-
-            // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
-            // animate in from this position. Since the animations are chained, when the new bubble
-            // flies in from the side, it will push the other ones out of the way.
-            float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
-            child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+            // If this is the first child added, position the stack in its starting position before
+            // animating in.
+            moveStackToStartPosition(() -> animateInBubble(child));
+        } else if (mLayout.indexOfChild(child) == 0) {
+            // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
+            // to the back of the stack, it'll be largely invisible so don't bother animating it in.
+            animateInBubble(child);
         }
     }
 
@@ -334,10 +309,14 @@
     }
 
     /** Moves the stack, without any animation, to the starting position. */
-    private void moveStackToStartPosition() {
-        mLayout.post(() -> setStackPosition(
-                getAllowableStackPositionRegion().right,
-                getAllowableStackPositionRegion().top + mStackStartingVerticalOffset));
+    private void moveStackToStartPosition(Runnable after) {
+        // Post to ensure that the layout's width and height have been calculated.
+        mLayout.post(() -> {
+            setStackPosition(
+                    getAllowableStackPositionRegion().right,
+                    getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
+            after.run();
+        });
     }
 
     /**
@@ -379,6 +358,29 @@
         }
     }
 
+    /** Animates in the given bubble. */
+    private void animateInBubble(View child) {
+        child.setTranslationY(mStackPosition.y);
+
+        // Pop in the new bubble.
+        child.setScaleX(ANIMATE_IN_STARTING_SCALE);
+        child.setScaleY(ANIMATE_IN_STARTING_SCALE);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
+
+        // Fade in the new bubble.
+        child.setAlpha(0);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
+
+        // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
+        // animate in from this position. Since the animations are chained, when the new bubble
+        // flies in from the side, it will push the other ones out of the way.
+        float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+        child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+    }
+
     /**
      * Springs the first bubble to the given final position, with the rest of the stack 'following'.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 900ea72..a74c328 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -61,7 +61,7 @@
 
     @VisibleForTesting
     public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) {
-        super(context, type);
+        super(context, type, false /* immediately */);
         mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
         mWpHiddenColors = new GradientColors();
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
index 4a2e06c..f5cf149 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
@@ -25,9 +25,4 @@
      * Invoked every time a minute is elapsed in doze mode
      */
     void dozeTimeTick();
-
-    /**
-     * When view is double tapped in doze mode.
-     */
-    void onDozeDoubleTap();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 5efdc2f..2d1dba6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -131,7 +131,7 @@
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
                         Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
-                        mConfig.wakeScreenGestureAvailable(),
+                        mConfig.wakeScreenGestureAvailable() && alwaysOn,
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 873fbc2..e207cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -161,7 +161,8 @@
                 } else {
                     mDozeHost.extendPulse();
                 }
-            }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason);
+            }, sensorPerformedProxCheck
+                    || (mDockManager != null && mDockManager.isDocked()), pulseReason);
         }
 
         if (isPickup) {
@@ -224,7 +225,9 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
-                mDockManager.addListener(mDockEventListener);
+                if (mDockManager != null) {
+                    mDockManager.addListener(mDockEventListener);
+                }
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -250,7 +253,9 @@
             case FINISH:
                 mBroadcastReceiver.unregister(mContext);
                 mDozeHost.removeCallback(mHostCallback);
-                mDockManager.removeListener(mDockEventListener);
+                if (mDockManager != null) {
+                    mDockManager.removeListener(mDockEventListener);
+                }
                 mDozeSensors.setListening(false);
                 mDozeSensors.setProxListening(false);
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index f5ac0d3..3fa6035 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -91,6 +91,7 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.EmergencyDialerConstants;
+import com.android.systemui.util.leak.RotationUtils;
 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
@@ -1091,7 +1092,7 @@
         }
 
         protected int getActionLayoutId(Context context) {
-            if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+            if (isGridEnabled(context)) {
                 return com.android.systemui.R.layout.global_actions_grid_item;
             }
             return com.android.systemui.R.layout.global_actions_item;
@@ -1465,7 +1466,7 @@
 
         private final Context mContext;
         private final MyAdapter mAdapter;
-        private final MultiListLayout mGlobalActionsLayout;
+        private MultiListLayout mGlobalActionsLayout;
         private final OnClickListener mClickListener;
         private final OnItemLongClickListener mLongClickListener;
         private final GradientDrawable mGradientDrawable;
@@ -1505,8 +1506,13 @@
             window.setBackgroundDrawable(mGradientDrawable);
             window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
 
+            initializeLayout();
 
-            setContentView(getGlobalActionsLayoutId(context));
+            setTitle(R.string.global_actions);
+        }
+
+        private void initializeLayout() {
+            setContentView(getGlobalActionsLayoutId(mContext));
             mGlobalActionsLayout = (MultiListLayout)
                     findViewById(com.android.systemui.R.id.global_actions_view);
             mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
@@ -1520,11 +1526,14 @@
                     return true;
                 }
             });
-            setTitle(R.string.global_actions);
+            mGlobalActionsLayout.setRotationListener(this::onRotate);
         }
 
         private int getGlobalActionsLayoutId(Context context) {
-            if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+            if (isGridEnabled(context)) {
+                if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+                    return com.android.systemui.R.layout.global_actions_grid_seascape;
+                }
                 return com.android.systemui.R.layout.global_actions_grid;
             }
             return com.android.systemui.R.layout.global_actions_wrapped;
@@ -1543,10 +1552,20 @@
                 int separatedIndex = separatedActions.indexOf(action);
                 ViewGroup parent;
                 if (separatedIndex != -1) {
-                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex);
+                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false);
                 } else {
+                    boolean reverse = false;
+
+                    // If we're using the grid layout and we're in seascape, reverse the order
+                    // of sublists to make sure they render in the correct positions,
+                    // since we can't reverse vertical LinearLayouts through the layout xml.
+
+                    if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext)
+                            == RotationUtils.ROTATION_SEASCAPE) {
+                        reverse = true;
+                    }
                     int listIndex = listActions.indexOf(action);
-                    parent = mGlobalActionsLayout.getParentView(false, listIndex);
+                    parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse);
                 }
                 View v = mAdapter.getView(i, null, parent);
                 final int pos = i;
@@ -1664,5 +1683,19 @@
         public void setKeyguardShowing(boolean keyguardShowing) {
             mKeyguardShowing = keyguardShowing;
         }
+
+        public void onRotate(int from, int to) {
+            if (mShowing && isGridEnabled(mContext)) {
+                initializeLayout();
+                updateList();
+            }
+        }
+    }
+
+    /**
+     * Determines whether or not the Global Actions Dialog should use the newer grid-style layout.
+     */
+    public static boolean isGridEnabled(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 0e49b5f..1d04277 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -83,11 +83,11 @@
     }
 
     @Override
-    public ViewGroup getParentView(boolean separated, int index) {
+    public ViewGroup getParentView(boolean separated, int index, boolean reverseOrder) {
         if (separated) {
             return getSeparatedView();
         } else {
-            return getListView().getParentView(index);
+            return getListView().getParentView(index, reverseOrder);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
index 3775515..d5dcd74 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
@@ -28,16 +28,13 @@
  *
  * * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item
  *   count.
+ * * Determine the position and parent of any item by its index and the total item count.
  * * Display and hide sub-lists as needed, depending on the expected item count.
- * * Favor bias toward having more rows or columns depending on the orientation of the device
- *   (TODO(123344999): Implement this, currently always favors adding more rows.)
- * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows
- *   or columns depending on the orientation of the device.
- *   (TODO(123344999): Implement this, currently always columns.)
  *
  * While we could implement this behavior with a GridLayout, it would take significantly more
  * time and effort, and would require more substantial refactoring of the existing code in
- * GlobalActionsDialog, since it would require manipulation of the child items themselves.
+ * GlobalActionsDialog, since it would require manipulation of layout properties on the child items
+ * themselves.
  *
  */
 
@@ -65,14 +62,25 @@
     /**
      * Get the parent view associated with the item which should be placed at the given position.
      */
-    public ViewGroup getParentView(int index) {
-        ViewGroup firstParent = (ViewGroup) getChildAt(0);
+    public ViewGroup getParentView(int index, boolean reverseSublists) {
         if (mRows == 0) {
-            return firstParent;
+            return null;
         }
+        int column = getParentViewIndex(index, reverseSublists);
+        return (ViewGroup) getChildAt(column);
+    }
+
+    private int reverseSublistIndex(int index) {
+        return getChildCount() - (index + 1);
+    }
+
+    private int getParentViewIndex(int index, boolean reverseSublists) {
         int column = (int) Math.floor(index / mRows);
-        ViewGroup parent = (ViewGroup) getChildAt(column);
-        return parent != null ? parent : firstParent;
+        int columnCount = getChildCount();
+        if (reverseSublists) {
+            column = reverseSublistIndex(column);
+        }
+        return column;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
new file mode 100644
index 0000000..d03b00b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
+import static android.opengl.GLES20.GL_VERTEX_SHADER;
+import static android.opengl.GLES20.glAttachShader;
+import static android.opengl.GLES20.glCompileShader;
+import static android.opengl.GLES20.glCreateProgram;
+import static android.opengl.GLES20.glCreateShader;
+import static android.opengl.GLES20.glGetAttribLocation;
+import static android.opengl.GLES20.glGetUniformLocation;
+import static android.opengl.GLES20.glLinkProgram;
+import static android.opengl.GLES20.glShaderSource;
+import static android.opengl.GLES20.glUseProgram;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
+ */
+class ImageGLProgram {
+    private static final String TAG = ImageGLProgram.class.getSimpleName();
+
+    private Context mContext;
+    private int mProgramHandle;
+
+    ImageGLProgram(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    private int loadShaderProgram(int vertexId, int fragmentId) {
+        final String vertexSrc = getShaderResource(vertexId);
+        final String fragmentSrc = getShaderResource(fragmentId);
+        final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
+        final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
+        return getProgramHandle(vertexHandle, fragmentHandle);
+    }
+
+    private String getShaderResource(int shaderId) {
+        Resources res = mContext.getResources();
+        StringBuilder code = new StringBuilder();
+
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(res.openRawResource(shaderId)))) {
+            String nextLine;
+            while ((nextLine = reader.readLine()) != null) {
+                code.append(nextLine).append("\n");
+            }
+        } catch (IOException | Resources.NotFoundException ex) {
+            Log.d(TAG, "Can not read the shader source", ex);
+            code = null;
+        }
+
+        return code == null ? "" : code.toString();
+    }
+
+    private int getShaderHandle(int type, String src) {
+        final int shader = glCreateShader(type);
+        if (shader == 0) {
+            Log.d(TAG, "Create shader failed, type=" + type);
+            return 0;
+        }
+        glShaderSource(shader, src);
+        glCompileShader(shader);
+        return shader;
+    }
+
+    private int getProgramHandle(int vertexHandle, int fragmentHandle) {
+        final int program = glCreateProgram();
+        if (program == 0) {
+            Log.d(TAG, "Can not create OpenGL ES program");
+            return 0;
+        }
+
+        glAttachShader(program, vertexHandle);
+        glAttachShader(program, fragmentHandle);
+        glLinkProgram(program);
+        return program;
+    }
+
+    boolean useGLProgram(int vertexResId, int fragmentResId) {
+        mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
+        glUseProgram(mProgramHandle);
+        return true;
+    }
+
+    int getAttributeHandle(String name) {
+        return glGetAttribLocation(mProgramHandle, name);
+    }
+
+    int getUniformHandle(String name) {
+        return glGetUniformLocation(mProgramHandle, name);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
new file mode 100644
index 0000000..19d85b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2019 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_FLOAT;
+import static android.opengl.GLES20.GL_LINEAR;
+import static android.opengl.GLES20.GL_TEXTURE0;
+import static android.opengl.GLES20.GL_TEXTURE_2D;
+import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
+import static android.opengl.GLES20.GL_TRIANGLES;
+import static android.opengl.GLES20.glActiveTexture;
+import static android.opengl.GLES20.glBindTexture;
+import static android.opengl.GLES20.glDrawArrays;
+import static android.opengl.GLES20.glEnableVertexAttribArray;
+import static android.opengl.GLES20.glGenTextures;
+import static android.opengl.GLES20.glTexParameteri;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glVertexAttribPointer;
+
+import android.graphics.Bitmap;
+import android.opengl.GLUtils;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * This class takes charge of the geometry data like vertices and texture coordinates.
+ * It delivers these data to opengl runtime and triggers draw calls if necessary.
+ */
+class ImageGLWallpaper {
+    private static final String TAG = ImageGLWallpaper.class.getSimpleName();
+
+    static final String A_POSITION = "aPosition";
+    static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
+    static final String U_CENTER_REVEAL = "uCenterReveal";
+    static final String U_REVEAL = "uReveal";
+    static final String U_AOD2OPACITY = "uAod2Opacity";
+    static final String U_TEXTURE = "uTexture";
+
+    private static final int HANDLE_UNDEFINED = -1;
+    private static final int POSITION_COMPONENT_COUNT = 2;
+    private static final int TEXTURE_COMPONENT_COUNT = 2;
+    private static final int BYTES_PER_FLOAT = 4;
+
+    // Vertices to define the square with 2 triangles.
+    private static final float[] VERTICES = {
+            -1.0f,  -1.0f,
+            +1.0f,  -1.0f,
+            +1.0f,  +1.0f,
+            +1.0f,  +1.0f,
+            -1.0f,  +1.0f,
+            -1.0f,  -1.0f
+    };
+
+    // Texture coordinates that maps to vertices.
+    private static final float[] TEXTURES = {
+            0f, 1f,
+            1f, 1f,
+            1f, 0f,
+            1f, 0f,
+            0f, 0f,
+            0f, 1f
+    };
+
+    private final FloatBuffer mVertexBuffer;
+    private final FloatBuffer mTextureBuffer;
+    private final ImageGLProgram mProgram;
+
+    private int mAttrPosition;
+    private int mAttrTextureCoordinates;
+    private int mUniAod2Opacity;
+    private int mUniCenterReveal;
+    private int mUniReveal;
+    private int mUniTexture;
+    private int mTextureId;
+
+    ImageGLWallpaper(ImageGLProgram program) {
+        mProgram = program;
+
+        // Create an float array in opengles runtime (native) and put vertex data.
+        mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT)
+            .order(ByteOrder.nativeOrder())
+            .asFloatBuffer();
+        mVertexBuffer.put(VERTICES);
+        mVertexBuffer.position(0);
+
+        // Create an float array in opengles runtime (native) and put texture data.
+        mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT)
+            .order(ByteOrder.nativeOrder())
+            .asFloatBuffer();
+        mTextureBuffer.put(TEXTURES);
+        mTextureBuffer.position(0);
+    }
+
+    void setup() {
+        setupAttributes();
+        setupUniforms();
+    }
+
+    private void setupAttributes() {
+        mAttrPosition = mProgram.getAttributeHandle(A_POSITION);
+        mVertexBuffer.position(0);
+        glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT,
+                false, 0, mVertexBuffer);
+        glEnableVertexAttribArray(mAttrPosition);
+
+        mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES);
+        mTextureBuffer.position(0);
+        glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT,
+                false, 0, mTextureBuffer);
+        glEnableVertexAttribArray(mAttrTextureCoordinates);
+    }
+
+    private void setupUniforms() {
+        mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
+        mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL);
+        mUniReveal = mProgram.getUniformHandle(U_REVEAL);
+        mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
+    }
+
+    int getHandle(String name) {
+        switch (name) {
+            case A_POSITION:
+                return mAttrPosition;
+            case A_TEXTURE_COORDINATES:
+                return mAttrTextureCoordinates;
+            case U_AOD2OPACITY:
+                return mUniAod2Opacity;
+            case U_CENTER_REVEAL:
+                return mUniCenterReveal;
+            case U_REVEAL:
+                return mUniReveal;
+            case U_TEXTURE:
+                return mUniTexture;
+            default:
+                return HANDLE_UNDEFINED;
+        }
+    }
+
+    void draw() {
+        glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
+    }
+
+    void setupTexture(Bitmap bitmap) {
+        final int[] tids = new int[1];
+
+        if (bitmap == null) {
+            Log.w(TAG, "setupTexture: invalid bitmap");
+            return;
+        }
+
+        // Generate one texture object and store the id in tids[0].
+        glGenTextures(1, tids, 0);
+        if (tids[0] == 0) {
+            Log.w(TAG, "setupTexture: glGenTextures() failed");
+            return;
+        }
+
+        // Bind a named texture to a texturing target.
+        glBindTexture(GL_TEXTURE_2D, tids[0]);
+        // Load the bitmap data and copy it over into the texture object that is currently bound.
+        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+        // Use bilinear texture filtering when minification.
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        // Use bilinear texture filtering when magnification.
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        mTextureId = tids[0];
+    }
+
+    void useTexture() {
+        // Set the active texture unit to texture unit 0.
+        glActiveTexture(GL_TEXTURE0);
+        // Bind the texture to this unit.
+        glBindTexture(GL_TEXTURE_2D, mTextureId);
+        // Let the texture sampler in fragment shader to read form this texture unit.
+        glUniform1i(mUniTexture, 0);
+    }
+
+    void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight,
+            float xOffset, float yOffset) {
+        if (bitmap == null) {
+            Log.d(TAG, "adjustTextureCoordinates: invalid bitmap");
+            return;
+        }
+
+        int bitmapWidth = bitmap.getWidth();
+        int bitmapHeight = bitmap.getHeight();
+        float ratioW = 1f;
+        float ratioH = 1f;
+        float rX = 0f;
+        float rY = 0f;
+        float[] coordinates = null;
+
+        final boolean adjustWidth = bitmapWidth > surfaceWidth;
+        final boolean adjustHeight = bitmapHeight > surfaceHeight;
+
+        if (adjustWidth || adjustHeight) {
+            coordinates = TEXTURES.clone();
+        }
+
+        if (adjustWidth) {
+            float x = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset) / bitmapWidth;
+            ratioW = (float) surfaceWidth / bitmapWidth;
+            float referenceX = x + ratioW > 1f ? 1f - ratioW : x;
+            for (int i = 0; i < coordinates.length; i += 2) {
+                if (i == 2 || i == 4 || i == 6) {
+                    coordinates[i] = Math.min(1f, referenceX + ratioW);
+                } else {
+                    coordinates[i] = referenceX;
+                }
+            }
+            rX = referenceX;
+        }
+
+
+        if (adjustHeight) {
+            float y = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset) / bitmapHeight;
+            ratioH = (float) surfaceHeight / bitmapHeight;
+            float referenceY = y + ratioH > 1f ? 1f - ratioH : y;
+            for (int i = 1; i < coordinates.length; i += 2) {
+                if (i == 1 || i == 3 || i == 11) {
+                    coordinates[i] = Math.min(1f, referenceY + ratioH);
+                } else {
+                    coordinates[i] = referenceY;
+                }
+            }
+            rY = referenceY;
+        }
+
+        if (adjustWidth || adjustHeight) {
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, "adjustTextureCoordinates: sW=" + surfaceWidth + ", sH=" + surfaceHeight
+                        + ", bW=" + bitmapWidth + ", bH=" + bitmapHeight
+                        + ", rW=" + ratioW + ", rH=" + ratioH + ", rX=" + rX + ", rY=" + rY);
+            }
+            mTextureBuffer.put(coordinates);
+            mTextureBuffer.position(0);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
new file mode 100644
index 0000000..477e7d7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 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.glwallpaper;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * A helper class that computes histogram and percentile 85 from a bitmap.
+ * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ */
+class ImageProcessHelper {
+    private static final String TAG = ImageProcessHelper.class.getSimpleName();
+    private static final float DEFAULT_PER85 = 0.8f;
+    private static final int MSG_UPDATE_PER85 = 1;
+
+    /**
+     * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
+     * Luminance = .2126f * r + .7152f * g + .0722f * b.
+     */
+    private static final float[] LUMINOSITY_MATRIX = new float[] {
+            .2126f,     .0000f,     .0000f,     .0000f,     .0000f,
+            .0000f,     .7152f,     .0000f,     .0000f,     .0000f,
+            .0000f,     .0000f,     .0722f,     .0000f,     .0000f,
+            .0000f,     .0000f,     .0000f,     1.000f,     .0000f
+    };
+
+    private final Handler mHandler = new Handler(new Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_PER85:
+                    mPer85 = (float) msg.obj;
+                    return true;
+                default:
+                    return false;
+            }
+        }
+    });
+
+    private float mPer85 = DEFAULT_PER85;
+
+    void startComputingPercentile85(Bitmap bitmap) {
+        new Per85ComputeTask(mHandler).execute(bitmap);
+    }
+
+    float getPercentile85() {
+        return mPer85;
+    }
+
+    private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+        private Handler mUpdateHandler;
+
+        Per85ComputeTask(Handler handler) {
+            super(handler);
+            mUpdateHandler = handler;
+        }
+
+        @Override
+        protected Float doInBackground(Bitmap... bitmaps) {
+            Bitmap bitmap = bitmaps[0];
+            if (bitmap != null) {
+                int[] histogram = processHistogram(bitmap);
+                return computePercentile85(bitmap, histogram);
+            }
+            Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
+            return DEFAULT_PER85;
+        }
+
+        @Override
+        protected void onPostExecute(Float result) {
+            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+            mUpdateHandler.sendMessage(msg);
+        }
+
+        private int[] processHistogram(Bitmap bitmap) {
+            int width = bitmap.getWidth();
+            int height = bitmap.getHeight();
+
+            Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
+            Canvas canvas = new Canvas(target);
+            ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
+            Paint paint = new Paint();
+            paint.setColorFilter(new ColorMatrixColorFilter(cm));
+            canvas.drawBitmap(bitmap, new Matrix(), paint);
+
+            // TODO: Fine tune the performance here, tracking on b/123615079.
+            int[] histogram = new int[256];
+            for (int row = 0; row < height; row++) {
+                for (int col = 0; col < width; col++) {
+                    int pixel = target.getPixel(col, row);
+                    int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
+                    histogram[y]++;
+                }
+            }
+
+            return histogram;
+        }
+
+        private float computePercentile85(Bitmap bitmap, int[] histogram) {
+            float per85 = DEFAULT_PER85;
+            int pixelCount = bitmap.getWidth() * bitmap.getHeight();
+            float[] acc = new float[256];
+            for (int i = 0; i < acc.length; i++) {
+                acc[i] = (float) histogram[i] / pixelCount;
+                float prev = i == 0 ? 0f : acc[i - 1];
+                float next = acc[i];
+                float idx = (float) (i + 1) / 255;
+                float sum = prev + next;
+                if (prev < 0.85f && sum >= 0.85f) {
+                    per85 = idx;
+                }
+                if (i > 0) {
+                    acc[i] += acc[i - 1];
+                }
+            }
+            return per85;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
new file mode 100644
index 0000000..5914236
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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.glwallpaper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.systemui.Interpolators;
+
+/**
+ * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
+ * The transition will happen while getting awake and quit events.
+ */
+class ImageRevealHelper {
+    private static final String TAG = ImageRevealHelper.class.getSimpleName();
+    private static final float MAX_REVEAL = 0f;
+    private static final float MIN_REVEAL = 1f;
+
+    private final ValueAnimator mAnimator;
+    private final RevealStateListener mRevealListener;
+    private float mReveal = MAX_REVEAL;
+    private boolean mAwake = false;
+
+    ImageRevealHelper(RevealStateListener listener) {
+        mRevealListener = listener;
+        mAnimator = ValueAnimator.ofFloat();
+        mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mAnimator.addUpdateListener(animator -> {
+            mReveal = (float) animator.getAnimatedValue();
+            if (mRevealListener != null) {
+                mRevealListener.onRevealStateChanged();
+            }
+        });
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mIsCanceled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mIsCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mIsCanceled) {
+                    mAwake = !mAwake;
+                }
+                mIsCanceled = false;
+            }
+        });
+    }
+
+    private void animate() {
+        mAnimator.cancel();
+        mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
+        mAnimator.start();
+    }
+
+    public float getReveal() {
+        return mReveal;
+    }
+
+    public boolean isAwake() {
+        return mAwake;
+    }
+
+    void updateAwake(boolean awake, long duration) {
+        mAwake = awake;
+        mAnimator.setDuration(duration);
+        animate();
+    }
+
+    /**
+     * A listener to trace value changes of reveal.
+     */
+    public interface RevealStateListener {
+
+        /**
+         * Called back while reveal status changes.
+         */
+        void onRevealStateChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
new file mode 100644
index 0000000..991b116
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
+import static android.opengl.GLES20.glClear;
+import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glUniform1f;
+import static android.opengl.GLES20.glViewport;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.opengl.GLSurfaceView;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.systemui.ImageWallpaper;
+import com.android.systemui.ImageWallpaper.ImageGLView;
+import com.android.systemui.R;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A GL renderer for image wallpaper.
+ */
+public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
+        ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener {
+    private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
+
+    private final WallpaperManager mWallpaperManager;
+    private final ImageGLProgram mProgram;
+    private final ImageGLWallpaper mWallpaper;
+    private final ImageProcessHelper mImageProcessHelper;
+    private final ImageRevealHelper mImageRevealHelper;
+    private final ImageGLView mGLView;
+    private float mXOffset = 0f;
+    private float mYOffset = 0f;
+
+    public ImageWallpaperRenderer(Context context, ImageGLView glView) {
+        mWallpaperManager = context.getSystemService(WallpaperManager.class);
+        if (mWallpaperManager == null) {
+            Log.w(TAG, "WallpaperManager not available");
+        }
+
+        mProgram = new ImageGLProgram(context);
+        mWallpaper = new ImageGLWallpaper(mProgram);
+        mImageProcessHelper = new ImageProcessHelper();
+        mImageRevealHelper = new ImageRevealHelper(this);
+        mGLView = glView;
+
+        if (mWallpaperManager != null) {
+            // Compute per85 as transition threshold, this is an async work.
+            mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap());
+        }
+    }
+
+    @Override
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        glClearColor(0f, 0f, 0f, 1.0f);
+        mProgram.useGLProgram(
+                R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
+        mWallpaper.setup();
+        mWallpaper.setupTexture(mWallpaperManager.getBitmap());
+    }
+
+    @Override
+    public void onSurfaceChanged(GL10 gl, int width, int height) {
+        glViewport(0, 0, width, height);
+        if (Build.IS_DEBUGGABLE) {
+            Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height
+                    + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
+        }
+        mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(),
+                width, height, mXOffset, mYOffset);
+    }
+
+    @Override
+    public void onDrawFrame(GL10 gl) {
+        float threshold = mImageProcessHelper.getPercentile85();
+        float reveal = mImageRevealHelper.getReveal();
+
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
+
+        mWallpaper.useTexture();
+        mWallpaper.draw();
+    }
+
+    @Override
+    public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
+        mImageRevealHelper.updateAwake(!inAmbientMode, duration);
+        requestRender();
+    }
+
+    @Override
+    public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
+        if (frame == null || mWallpaperManager == null
+                || (xOffset == mXOffset && yOffset == mYOffset)) {
+            return;
+        }
+
+        Bitmap bitmap = mWallpaperManager.getBitmap();
+        if (bitmap == null) {
+            return;
+        }
+
+        int width = frame.width();
+        int height = frame.height();
+        mXOffset = xOffset;
+        mYOffset = yOffset;
+
+        if (Build.IS_DEBUGGABLE) {
+            Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height
+                    + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
+        }
+        mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset);
+        requestRender();
+    }
+
+    @Override
+    public void onRevealStateChanged() {
+        requestRender();
+    }
+
+    private void requestRender() {
+        if (mGLView != null) {
+            mGLView.render();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 684175c..85d975f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -25,7 +25,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
@@ -35,10 +37,13 @@
 import android.os.Trace;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
-import android.text.Spannable;
 import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
 import android.text.style.StyleSpan;
+import android.util.MathUtils;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -72,6 +77,8 @@
 
     private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
+    private static final String KEYGUARD_HEADER_URI =
+            "content://com.android.systemui.keyguard/header";
     public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
     public static final String KEYGUARD_NEXT_ALARM_URI =
             "content://com.android.systemui.keyguard/alarm";
@@ -90,6 +97,7 @@
     private static KeyguardSliceProvider sInstance;
 
     protected final Uri mSliceUri;
+    protected final Uri mHeaderUri;
     protected final Uri mDateUri;
     protected final Uri mAlarmUri;
     protected final Uri mDndUri;
@@ -163,6 +171,7 @@
     KeyguardSliceProvider(Handler handler) {
         mHandler = handler;
         mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
+        mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
         mDateUri = Uri.parse(KEYGUARD_DATE_URI);
         mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
         mDndUri = Uri.parse(KEYGUARD_DND_URI);
@@ -213,26 +222,32 @@
     protected void addMediaLocked(ListBuilder listBuilder) {
         if (mMediaMetaData != null) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
+
+            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+            if (notificationIcon != null) {
+                Drawable drawable = notificationIcon.loadDrawable(getContext());
+                Rect mediaBounds = new Rect(0 /* left */, 0 /* top */,
+                        drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+                int iconHeaderSize = getContext().getResources()
+                        .getDimensionPixelSize(R.dimen.header_icon_size);
+                MathUtils.fitRect(mediaBounds, iconHeaderSize);
+                drawable.setBounds(mediaBounds);
+                builder.append("# ");
+                builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER),
+                        0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            }
+
             CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
             if (TextUtils.isEmpty(title)) {
                 title = getContext().getResources().getString(R.string.music_controls_no_title);
             }
             builder.append(title);
-            builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+            listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder));
 
             CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
             if (!TextUtils.isEmpty(album)) {
-                builder.append("  ").append(album);
+                listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album));
             }
-
-            RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
-            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
-            if (notificationIcon != null) {
-                IconCompat icon = IconCompat.createFromIcon(notificationIcon);
-                mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
-            }
-
-            listBuilder.addRow(mediaBuilder);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index df76315..9bca2cc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -40,19 +40,15 @@
 import android.util.Log;
 import android.view.Window;
 import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
 
 import com.android.systemui.R;
 
 public class MediaProjectionPermissionActivity extends Activity
-        implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener,
-        DialogInterface.OnCancelListener {
+        implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     private static final String TAG = "MediaProjectionPermissionActivity";
     private static final float MAX_APP_NAME_SIZE_PX = 500f;
     private static final String ELLIPSIS = "\u2026";
 
-    private boolean mPermanentGrant;
     private String mPackageName;
     private int mUid;
     private IMediaProjectionManager mService;
@@ -85,8 +81,7 @@
 
         try {
             if (mService.hasProjectionPermission(mUid, mPackageName)) {
-                setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName,
-                        false /*permanentGrant*/));
+                setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
                 finish();
                 return;
             }
@@ -136,19 +131,20 @@
                     appNameIndex, appNameIndex + appName.length(), 0);
         }
 
+        String dialogTitle = getString(R.string.media_projection_dialog_title, appName);
+
         mDialog = new AlertDialog.Builder(this)
+                .setTitle(dialogTitle)
                 .setIcon(aInfo.loadIcon(packageManager))
                 .setMessage(message)
                 .setPositiveButton(R.string.media_projection_action_text, this)
                 .setNegativeButton(android.R.string.cancel, this)
-                .setView(R.layout.remember_permission_checkbox)
                 .setOnCancelListener(this)
                 .create();
 
         mDialog.create();
         mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
 
-        ((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this);
         final Window w = mDialog.getWindow();
         w.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
         w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -168,8 +164,7 @@
     public void onClick(DialogInterface dialog, int which) {
         try {
             if (which == AlertDialog.BUTTON_POSITIVE) {
-                setResult(RESULT_OK, getMediaProjectionIntent(
-                        mUid, mPackageName, mPermanentGrant));
+                setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error granting projection permission", e);
@@ -182,15 +177,10 @@
         }
     }
 
-    @Override
-    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-        mPermanentGrant = isChecked;
-    }
-
-    private Intent getMediaProjectionIntent(int uid, String packageName, boolean permanentGrant)
+    private Intent getMediaProjectionIntent(int uid, String packageName)
             throws RemoteException {
         IMediaProjection projection = mService.createProjection(uid, packageName,
-                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, permanentGrant);
+                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
         Intent intent = new Intent();
         intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder());
         return intent;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index ecbf024..15dc43f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -16,12 +16,13 @@
 
 import android.content.Context
 import android.util.AttributeSet
-import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
+import com.android.systemui.Dependency
 import com.android.systemui.R
+import com.android.systemui.statusbar.policy.KeyguardMonitor
 
 class OngoingPrivacyChip @JvmOverloads constructor(
     context: Context,
@@ -38,10 +39,12 @@
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
     private val iconColor = context.resources.getColor(
             R.color.status_bar_clock_color, context.theme)
+    private val sidePadding =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
     private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
     private lateinit var text: TextView
     private lateinit var iconsContainer: LinearLayout
-    private lateinit var inUseText: TextView
+    private lateinit var back: LinearLayout
     var expanded = false
         set(value) {
             if (value != field) {
@@ -49,6 +52,8 @@
                 updateView()
             }
         }
+    @Suppress("DEPRECATION")
+    private val keyguardMonitor = Dependency.get(KeyguardMonitor::class.java)
     var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
     var privacyList = emptyList<PrivacyItem>()
         set(value) {
@@ -60,15 +65,15 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
 
-        inUseText = findViewById(R.id.in_use_text)
+        back = findViewById(R.id.background)
         text = findViewById(R.id.text_container)
         iconsContainer = findViewById(R.id.icons_container)
     }
 
     // Should only be called if the builder icons or app changed
     private fun updateView() {
-        inUseText.visibility = if (expanded) View.GONE else View.VISIBLE
-        background = if (expanded) backgroundDrawable else null
+        back.background = if (expanded) backgroundDrawable else null
+        back.setPaddingRelative(0, 0, if (expanded) sidePadding else 0, 0)
         fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
             iconsContainer.removeAllViews()
             dialogBuilder.generateIcons().forEachIndexed { i, it ->
@@ -90,16 +95,7 @@
         if (!privacyList.isEmpty()) {
             generateContentDescription()
             setIcons(builder, iconsContainer)
-            text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE
-            if (builder.types.size == 1 && expanded) {
-                if (builder.app != null) {
-                    text.setText(builder.app?.applicationName)
-                } else {
-                    text.text = context.resources.getQuantityString(
-                            R.plurals.ongoing_privacy_chip_multiple_apps,
-                            builder.appsAndTypes.size, builder.appsAndTypes.size)
-                }
-            }
+            setApplicationText()
         } else {
             text.visibility = GONE
             iconsContainer.removeAllViews()
@@ -107,13 +103,28 @@
         requestLayout()
     }
 
+    private fun setApplicationText() {
+        text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE
+        if (builder.types.size == 1 && expanded) {
+            if (builder.app != null && !amISecure()) {
+                text.setText(builder.app?.applicationName)
+            } else {
+                text.text = context.resources.getQuantityString(
+                        R.plurals.ongoing_privacy_chip_multiple_apps,
+                        builder.appsAndTypes.size, builder.appsAndTypes.size)
+            }
+        }
+    }
+
+    private fun amISecure() = keyguardMonitor.isShowing && keyguardMonitor.isSecure
+
     private fun generateContentDescription() {
         val typesText = builder.joinTypes()
         if (builder.types.size > 1) {
             contentDescription = context.getString(
                     R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
         } else {
-            if (builder.app != null) {
+            if (builder.app != null && !amISecure()) {
                 contentDescription =
                         context.getString(R.string.ongoing_privacy_chip_content_single_app,
                                 builder.app?.applicationName, typesText)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index cff7fe4..75b8a05 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -36,8 +36,8 @@
 import java.util.concurrent.TimeUnit
 
 class OngoingPrivacyDialog constructor(
-    val context: Context,
-    val dialogBuilder: PrivacyDialogBuilder
+    private val context: Context,
+    private val dialogBuilder: PrivacyDialogBuilder
 ) {
 
     private val iconSize = context.resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 9c1076a..bbea6b2 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -18,7 +18,7 @@
 import android.graphics.drawable.Drawable
 import com.android.systemui.R
 
-class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) {
+class PrivacyDialogBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
 
     val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
     val types: List<PrivacyType>
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index f7ca51d..a6e48f8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -62,4 +62,6 @@
             context.packageManager.getApplicationLabel(it) as String
         } ?: packageName
     }
+
+    override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f1c3bf2..625eacd 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -26,16 +26,26 @@
 import android.os.UserHandle
 import android.os.UserManager
 import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER_NAME
+import com.android.systemui.Dependency.MAIN_HANDLER_NAME
+import com.android.systemui.R
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
-import com.android.systemui.R
+import com.android.systemui.Dumpable
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import java.lang.ref.WeakReference
 import javax.inject.Inject
+import javax.inject.Named
 import javax.inject.Singleton
 
 @Singleton
-class PrivacyItemController @Inject constructor(val context: Context) {
+class PrivacyItemController @Inject constructor(
+        val context: Context,
+        private val appOpsController: AppOpsController,
+        @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
+        @Named(BG_HANDLER_NAME) private val bgHandler: Handler
+) : Dumpable {
 
     companion object {
         val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
@@ -48,16 +58,13 @@
         const val TAG = "PrivacyItemController"
         const val SYSTEM_UID = 1000
     }
-    private var privacyList = emptyList<PrivacyItem>()
 
-    @Suppress("DEPRECATION")
-    private val appOpsController = Dependency.get(AppOpsController::class.java)
+    @VisibleForTesting
+    internal var privacyList = emptyList<PrivacyItem>()
+        get() = field.toList() // Provides a shallow copy of the list
+
     private val userManager = context.getSystemService(UserManager::class.java)
     private var currentUserIds = emptyList<Int>()
-    @Suppress("DEPRECATION")
-    private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
-    @Suppress("DEPRECATION")
-    private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
     private var listening = false
     val systemApp =
             PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
@@ -188,4 +195,22 @@
             callback?.privacyChanged(list)
         }
     }
+
+    override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
+        pw?.println("PrivacyItemController state:")
+        pw?.println("  Listening: $listening")
+        pw?.println("  Current user ids: $currentUserIds")
+        pw?.println("  Privacy Items:")
+        privacyList.forEach {
+            pw?.print("    ")
+            pw?.println(it.toString())
+        }
+        pw?.println("  Callbacks:")
+        callbacks.forEach {
+            it.get()?.let {
+                pw?.print("    ")
+                pw?.println(it.toString())
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ee9255c..6a8c19a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -48,7 +48,6 @@
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -82,6 +81,7 @@
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -201,6 +201,8 @@
         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
+        // Ignore privacy icons because they show in the space above QQS
+        iconContainer.addIgnoredSlots(getIgnoredIconSlots());
         iconContainer.setShouldRestrictIcons(false);
         mIconManager = new TintedIconManager(iconContainer);
 
@@ -242,6 +244,18 @@
         updateShowPercent();
     }
 
+    private List<String> getIgnoredIconSlots() {
+        ArrayList<String> ignored = new ArrayList<>();
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_camera));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_microphone));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_location));
+
+        return ignored;
+    }
+
     private void updateStatusText() {
         boolean changed = updateRingerStatus() || updateAlarmStatus();
 
@@ -373,15 +387,6 @@
 
         setLayoutParams(lp);
 
-        if (mPrivacyChip != null) {
-            MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
-            int sideMargins = lm.leftMargin;
-            int topBottomMargins = resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_top_chip_margin);
-            lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
-            mPrivacyChip.setLayoutParams(lm);
-        }
-
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
         updatePrivacyChipAlphaAnimator();
@@ -553,12 +558,10 @@
             Handler mUiHandler = new Handler(Looper.getMainLooper());
             mUiHandler.post(() -> {
                 Dialog mDialog = new OngoingPrivacyDialog(mContext, builder).createDialog();
-                mDialog.getWindow().setType(
-                        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-                SystemUIDialog.setShowForAllUsers(mDialog, true);
+                SystemUIDialog.setShowForAllUsers(mDialog, false);
                 SystemUIDialog.registerDismissListener(mDialog);
                 SystemUIDialog.setWindowOnTop(mDialog);
-                mUiHandler.post(() -> mDialog.show());
+                mActivityStarter.postQSRunnableDismissingKeyguard(() -> mDialog.show());
                 mHost.collapsePanels();
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c474faf..83c4cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -24,6 +24,10 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_CHANNEL;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,7 +36,9 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -41,6 +47,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.InputChannel;
 import android.view.MotionEvent;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -51,6 +58,7 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -93,6 +101,8 @@
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
     private final Intent mQuickStepIntent;
 
+    private Region mActiveNavBarRegion;
+
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
     private @InteractionType int mInteractionFlags;
@@ -103,6 +113,8 @@
     private float mWindowCornerRadius;
     private boolean mSupportsRoundedCornersOnWindows;
 
+    private InputEventDispatcher mInputEventDispatcher;
+
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
         public void startScreenPinning(int taskId) {
@@ -309,6 +321,20 @@
                 mCurrentBoundedUserId = -1;
                 Log.e(TAG_OPS, "Failed to call onBind()", e);
             }
+
+            Bundle params = new Bundle();
+            params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
+            params.putParcelable(KEY_EXTRA_INPUT_CHANNEL, createNewInputDispatcher());
+            params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
+            params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
+            try {
+                mOverviewProxy.onInitialize(params);
+            } catch (RemoteException e) {
+                // Ignore error until the migration is complete.
+                Log.e(TAG_OPS, "Failed to call onBind()", e);
+            }
+            dispatchNavButtonBounds();
+
             notifyConnectionChanged();
         }
 
@@ -317,6 +343,7 @@
             Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
             mCurrentBoundedUserId = -1;
             retryConnectionWithBackoff();
+            disposeInputDispatcher();
         }
 
         @Override
@@ -324,15 +351,32 @@
             Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
             mCurrentBoundedUserId = -1;
             retryConnectionWithBackoff();
+            disposeInputDispatcher();
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
             // Do nothing
             mCurrentBoundedUserId = -1;
+            disposeInputDispatcher();
         }
     };
 
+    private void disposeInputDispatcher() {
+        if (mInputEventDispatcher != null) {
+            mInputEventDispatcher.dispose();
+            mInputEventDispatcher = null;
+        }
+    }
+
+    private InputChannel createNewInputDispatcher() {
+        disposeInputDispatcher();
+
+        InputChannel[] channels = InputChannel.openInputChannelPair("overview-proxy-service");
+        mInputEventDispatcher = new InputEventDispatcher(channels[0], Looper.getMainLooper());
+        return channels[1];
+    }
+
     private final DeviceProvisionedListener mDeviceProvisionedCallback =
                 new DeviceProvisionedListener() {
             @Override
@@ -382,6 +426,24 @@
         }
     }
 
+    /**
+     * Sets the navbar region which can receive touch inputs
+     */
+    public void onActiveNavBarRegionChanges(Region activeRegion) {
+        mActiveNavBarRegion = activeRegion;
+        dispatchNavButtonBounds();
+    }
+
+    private void dispatchNavButtonBounds() {
+        if (mOverviewProxy != null && mActiveNavBarRegion != null) {
+            try {
+                mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
+            } catch (RemoteException e) {
+                Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
+            }
+        }
+    }
+
     public float getBackButtonAlpha() {
         return mBackButtonAlpha;
     }
@@ -477,6 +539,10 @@
         return mOverviewProxy;
     }
 
+    public InputEventDispatcher getInputEventDispatcher() {
+        return mInputEventDispatcher;
+    }
+
     public int getInteractionFlags() {
         return mInteractionFlags;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 7a7d1f6..bb34a87 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -461,6 +461,9 @@
         if (mSnapAlgorithm == null) {
             mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
                     mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide);
+            if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) {
+                mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
+            }
         }
         if (mMinimizedSnapAlgorithm == null) {
             mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 2f19630..a6af82a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -28,7 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.util.stream.Stream;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index a3beb96..57d0588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -26,7 +26,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index 4944732..52b8cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -49,7 +49,7 @@
 
         context.display.getSize(mTmpSize)
         val renderScript = RenderScript.create(context)
-        val rect = Rect(0, 0,artwork.width, artwork.height)
+        val rect = Rect(0, 0, artwork.width, artwork.height)
         MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
         val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
                 true /* filter */)
@@ -67,6 +67,7 @@
         input.destroy()
         output.destroy()
         inBitmap.recycle()
+        blur.destroy()
 
         val canvas = Canvas(outBitmap)
         canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index e11ec2d..2edea78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -158,18 +158,6 @@
         });
     }
 
-    /** Removes navigation bars. */
-    public void destroy() {
-        mDisplayManager.unregisterDisplayListener(this);
-        if (mNavigationBars.size() > 0) {
-            for (int i = 0; i < mNavigationBars.size(); i++) {
-                int displayId = mNavigationBars.keyAt(i);
-                removeNavigationBar(displayId);
-            }
-            mNavigationBars.clear();
-        }
-    }
-
     private void removeNavigationBar(int displayId) {
         NavigationBarFragment navBar = mNavigationBars.get(displayId);
         if (navBar != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index c25b7cf..1cc6dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -42,8 +42,6 @@
     /** Adds a listener to be notified when the current user changes. */
     void addUserChangedListener(UserChangedListener listener);
 
-    void destroy();
-
     SparseArray<UserInfo> getCurrentProfiles();
 
     void setLockscreenPublicMode(boolean isProfilePublic, int userId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 6a49b80..9cb6f11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -550,12 +550,6 @@
         return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId);
     }
 
-    public void destroy() {
-        mContext.unregisterReceiver(mBaseBroadcastReceiver);
-        mContext.unregisterReceiver(mAllUsersReceiver);
-        mListeners.clear();
-    }
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NotificationLockscreenUserManager state:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 01b0bb1..110d515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -348,15 +348,13 @@
             if (notGoneIndex == 0) {
                 StatusBarIconView icon = row.getEntry().expandedIcon;
                 NotificationIconContainer.IconState iconState = getIconState(icon);
+                // The icon state might be null in rare cases where the notification is actually
+                // added to the layout, but not to the shelf. An example are replied messages, since
+                // they don't show up on AOD
                 if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
                     // only if the first icon is fully in the shelf we want to clip to it!
                     backgroundTop = (int) (row.getTranslationY() - getTranslationY());
                     firstElementRoundness = row.getCurrentTopRoundness();
-                } else if (iconState == null) {
-                    Log.wtf(TAG, "iconState is null. ExpandedIcon: " + row.getEntry().expandedIcon
-                            + (row.getEntry().expandedIcon != null
-                            ? "\n icon parent: " + row.getEntry().expandedIcon.getParent() : "")
-                            + " \n number of notifications: " + mHostLayout.getChildCount() );
                 }
             }
             if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 662cf51..ee5ac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -24,6 +24,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,7 +62,7 @@
     protected final NotificationLockscreenUserManager mLockscreenUserManager;
     protected final NotificationGroupManager mGroupManager;
     protected final VisualStabilityManager mVisualStabilityManager;
-    private final StatusBarStateControllerImpl mStatusBarStateController;
+    private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
 
     // Lazy
@@ -82,13 +83,13 @@
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotificationGroupManager groupManager,
             VisualStabilityManager visualStabilityManager,
-            StatusBarStateControllerImpl statusBarStateController,
+            StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
             Lazy<ShadeController> shadeController) {
         mLockscreenUserManager = notificationLockscreenUserManager;
         mGroupManager = groupManager;
         mVisualStabilityManager = visualStabilityManager;
-        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
         mShadeController = shadeController;
         Resources res = context.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 5605f3d..f6d3cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.notification;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.app.Notification;
 import android.service.notification.StatusBarNotification;
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -102,8 +102,7 @@
      * @param entry         entry to add
      * @param inflatedFlags flags representing content views that were inflated
      */
-    private void showAlertingView(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 839b06c..a5a6d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -20,7 +20,7 @@
 
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 /**
  * Listener interface for changes sent by NotificationEntryManager.
@@ -47,7 +47,11 @@
     }
 
     /**
-     * Called when a notification is updated, before any filtering of notifications have occurred.
+     * Called when a notification is about to be updated. Notification- and ranking-derived fields
+     * on the entry have already been updated but the following have not yet occurred:
+     * (a) View binding (i.e. the associated view has not yet been updated / inflation has not yet
+     *      been kicked off.
+     * (b) Notification filtering
      */
     default void onPreEntryUpdated(NotificationEntry entry) {
     }
@@ -61,8 +65,7 @@
     /**
      * Called when a notification's views are inflated for the first time.
      */
-    default void onEntryInflated(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 81d0e25..3fbc641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,8 +36,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -56,7 +56,7 @@
  */
 public class NotificationEntryManager implements
         Dumpable,
-        NotificationInflater.InflationCallback,
+        NotificationContentInflater.InflationCallback,
         NotificationUpdateHandler,
         VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
@@ -230,7 +230,6 @@
                 }
             }
         }
-        entry.setLowPriorityStateUpdated(false);
     }
 
     @Override
@@ -346,8 +345,7 @@
 
         Dependency.get(LeakDetector.class).trackInstance(entry);
         // Construct the expanded view.
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
-                mNotificationData.get(entry.key) != null);
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
 
         abortExistingInflation(key);
 
@@ -384,13 +382,11 @@
 
         mNotificationData.update(entry, ranking, notification);
 
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
-                mNotificationData.get(entry.key) != null);
-
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
         }
 
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
         updateNotifications();
 
         if (DEBUG) {
@@ -422,6 +418,7 @@
         }
     }
 
+    @Override
     public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
         List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(mNotificationData.getActiveNotifications());
@@ -447,8 +444,7 @@
                     entry,
                     oldImportances.get(entry.key),
                     oldAdjustments.get(entry.key),
-                    NotificationUiAdjustment.extractFromNotificationEntry(entry),
-                    mNotificationData.get(entry.key) != null);
+                    NotificationUiAdjustment.extractFromNotificationEntry(entry));
         }
 
         updateNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 88f4ca2..769cbb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -61,11 +61,6 @@
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
     }
 
-    /** Should be called when the list controller is being destroyed. */
-    public void destroy() {
-        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
-    }
-
     @SuppressWarnings("FieldCanBeLocal")
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 0b8596f..6f5baf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -19,8 +19,8 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -42,8 +42,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -78,7 +78,7 @@
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationInflater.InflationCallback mInflationCallback;
+    private NotificationContentInflater.InflationCallback mInflationCallback;
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
@@ -105,7 +105,7 @@
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager,
-            NotificationInflater.InflationCallback inflationCallback,
+            NotificationContentInflater.InflationCallback inflationCallback,
             BindRowCallback bindRowCallback) {
         mPresenter = presenter;
         mListContainer = listContainer;
@@ -124,8 +124,7 @@
      */
     public void inflateViews(
             NotificationEntry entry,
-            Runnable onDismissRunnable,
-            boolean isUpdate)
+            Runnable onDismissRunnable)
             throws InflationException {
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
@@ -135,13 +134,13 @@
         if (entry.rowExists()) {
             entry.updateIcons(mContext, sbn);
             entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
+            updateNotification(entry, pmUser, sbn, entry.getRow());
         } else {
             entry.createIcons(mContext, sbn);
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
-                        updateNotification(entry, pmUser, sbn, row, isUpdate);
+                        updateNotification(entry, pmUser, sbn, row);
                     });
         }
     }
@@ -197,15 +196,14 @@
             NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
-            NotificationUiAdjustment newAdjustment,
-            boolean isUpdate) {
+            NotificationUiAdjustment newAdjustment) {
         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
             if (entry.rowExists()) {
                 entry.reset();
                 PackageManager pmUser = StatusBar.getPackageManagerForUser(
                         mContext,
                         entry.notification.getUser().getIdentifier());
-                updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate);
+                updateNotification(entry, pmUser, entry.notification, entry.getRow());
             } else {
                 // Once the RowInflaterTask is done, it will pick up the updated entry, so
                 // no-op here.
@@ -224,12 +222,8 @@
             NotificationEntry entry,
             PackageManager pmUser,
             StatusBarNotification sbn,
-            ExpandableNotificationRow row,
-            boolean isUpdate) {
-        boolean isLowPriority = entry.ambient;
-        boolean wasLowPriority = row.isLowPriority();
-        row.setIsLowPriority(isLowPriority);
-        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+            ExpandableNotificationRow row) {
+        row.setIsLowPriority(entry.ambient);
         // bind the click event to the content area
         checkNotNull(mNotificationClicker).register(row, sbn);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index c886685..396a3a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -45,7 +45,7 @@
     private boolean mReorderingAllowed;
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
-    private ArraySet<View> mLowPriorityReorderingViews = new ArraySet<>();
+    private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
@@ -53,14 +53,21 @@
     public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryReinflated(NotificationEntry entry) {
-                if (entry.hasLowPriorityStateUpdated()) {
-                    onLowPriorityUpdated(entry);
-                    if (mPresenter != null) {
-                        mPresenter.updateNotificationViews();
-                    }
+            public void onPreEntryUpdated(NotificationEntry entry) {
+                final boolean mAmbientStateHasChanged =
+                        entry.ambient != entry.getRow().isLowPriority();
+                if (mAmbientStateHasChanged) {
+                    mLowPriorityReorderingViews.add(entry);
                 }
             }
+
+            @Override
+            public void onPostEntryUpdated(NotificationEntry entry) {
+                // This line is technically not required as we'll get called as the hierarchy
+                // manager will call onReorderingFinished() immediately before this.
+                // TODO: Find a way to make this relationship more explicit
+                mLowPriorityReorderingViews.remove(entry);
+            }
         });
     }
 
@@ -142,7 +149,7 @@
         if (mAddedChildren.contains(row)) {
             return true;
         }
-        if (mLowPriorityReorderingViews.contains(row)) {
+        if (mLowPriorityReorderingViews.contains(row.getEntry())) {
             return true;
         }
         if (mAllowedReorderViews.contains(row)
@@ -172,10 +179,6 @@
         }
     }
 
-    private void onLowPriorityUpdated(NotificationEntry entry) {
-        mLowPriorityReorderingViews.add(entry.getRow());
-    }
-
     /**
      * Notify the visual stability manager that a new view was added and should be allowed to
      * reorder next time.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f74de5b..3bf4d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -54,8 +54,8 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -515,7 +515,7 @@
         if (row != null) row.resetUserExpansion();
     }
 
-    public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
         if (row != null) row.freeContentViewWhenSafe(inflationFlag);
     }
 
@@ -622,10 +622,6 @@
         return null;
     }
 
-    public boolean hasLowPriorityStateUpdated() {
-        return row != null && row.hasLowPriorityStateUpdated();
-    }
-
     public void removeRow() {
         if (row != null) row.setRemoved();
     }
@@ -650,10 +646,6 @@
         return parent == null;
     }
 
-    public void setLowPriorityStateUpdated(boolean updated) {
-        if (row != null) row.setLowPriorityStateUpdated(updated);
-    }
-
     /**
      * @return Can the underlying notification be cleared? This can be different from whether the
      *         notification can be dismissed in case notifications are sensitive on the lockscreen.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b8e33a8..2b643d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,13 +17,13 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -89,7 +89,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -124,6 +124,7 @@
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
     private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
     private boolean mUpdateBackgroundOnUpdate;
+    private boolean mNotificationTranslationFinished = false;
 
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -133,8 +134,7 @@
     }
 
     private LayoutListener mLayoutListener;
-    private boolean mLowPriorityStateUpdated;
-    private final NotificationInflater mNotificationInflater;
+    private final NotificationContentInflater mNotificationInflater;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
     private int mMaxHeadsUpHeightBeforeN;
@@ -1213,6 +1213,7 @@
             l.initView();
             l.reInflateViews();
         }
+        mStatusBarNotification.clearPackageContext();
         mNotificationInflater.clearCachesAndReInflate();
         onNotificationUpdated();
     }
@@ -1453,6 +1454,10 @@
         return mIsBlockingHelperShowing;
     }
 
+    public boolean isBlockingHelperShowingAndTranslationFinished() {
+        return mIsBlockingHelperShowing && mNotificationTranslationFinished;
+    }
+
     public void setOnDismissRunnable(Runnable onDismissRunnable) {
         mOnDismissRunnable = onDismissRunnable;
     }
@@ -1577,15 +1582,6 @@
         }
     }
 
-
-    public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
-        mLowPriorityStateUpdated = lowPriorityStateUpdated;
-    }
-
-    public boolean hasLowPriorityStateUpdated() {
-        return mLowPriorityStateUpdated;
-    }
-
     public boolean isLowPriority() {
         return mIsLowPriority;
     }
@@ -1620,7 +1616,7 @@
     }
 
     @VisibleForTesting
-    public NotificationInflater getNotificationInflater() {
+    public NotificationContentInflater getNotificationInflater() {
         return mNotificationInflater;
     }
 
@@ -1635,7 +1631,7 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
-        mNotificationInflater = new NotificationInflater(this);
+        mNotificationInflater = new NotificationContentInflater(this);
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
                 new NotificationInlineImageCache());
@@ -1851,7 +1847,6 @@
     }
 
     void onGutsOpened() {
-        resetTranslation();
         updateContentAccessibilityImportanceForGuts(false /* isEnabled */);
     }
 
@@ -1905,11 +1900,10 @@
 
     @Override
     public void setTranslation(float translationX) {
-        if (areGutsExposed()) {
-            // Don't translate if guts are showing.
+        if (isBlockingHelperShowingAndTranslationFinished()) {
+            mGuts.setTranslationX(translationX);
             return;
-        }
-        if (!mShouldTranslateContents) {
+        } else if (!mShouldTranslateContents) {
             setTranslationX(translationX);
         } else if (mTranslateableViews != null) {
             // Translate the group of views
@@ -1925,6 +1919,7 @@
             // positioning, so we can use the scrollX instead.
             getEntry().expandedIcon.setScrollX((int) -translationX);
         }
+
         if (mMenuRow.getMenuView() != null) {
             mMenuRow.onParentTranslationUpdate(translationX);
         }
@@ -1936,6 +1931,10 @@
             return getTranslationX();
         }
 
+        if (isBlockingHelperShowingAndCanTranslate()) {
+            return mGuts.getTranslationX();
+        }
+
         if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
             // All of the views in the list should have same translation, just use first one.
             return mTranslateableViews.get(0).getTranslationX();
@@ -1944,15 +1943,16 @@
         return 0;
     }
 
+    private boolean isBlockingHelperShowingAndCanTranslate() {
+        return areGutsExposed() && mIsBlockingHelperShowing && mNotificationTranslationFinished;
+    }
+
     public Animator getTranslateViewAnimator(final float leftTarget,
             AnimatorUpdateListener listener) {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
         }
-        if (areGutsExposed()) {
-            // No translation if guts are exposed.
-            return null;
-        }
+
         final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
                 leftTarget);
         if (listener != null) {
@@ -1968,6 +1968,9 @@
 
             @Override
             public void onAnimationEnd(Animator anim) {
+                if (mIsBlockingHelperShowing) {
+                    mNotificationTranslationFinished = true;
+                }
                 if (!cancelled && leftTarget == 0) {
                     mMenuRow.resetMenu();
                     mTranslateAnim = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 42ebfce..b65c4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,9 +55,9 @@
 /**
  * A utility that inflates the right kind of contentView based on the state
  */
-public class NotificationInflater {
+public class NotificationContentInflater {
 
-    public static final String TAG = "NotificationInflater";
+    public static final String TAG = "NotifContentInflater";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
@@ -125,9 +125,10 @@
     private boolean mIsChildInGroup;
     private InflationCallback mCallback;
     private boolean mRedactAmbient;
+    private boolean mInflateSynchronously = false;
     private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
 
-    public NotificationInflater(ExpandableNotificationRow row) {
+    public NotificationContentInflater(ExpandableNotificationRow row) {
         mRow = row;
     }
 
@@ -232,8 +233,7 @@
      * will reinflate it.
      *
      * @param reInflateFlags flags which views should be inflated. Should be a subset of
-     *                       {@link NotificationInflater#mInflationFlags} as only those will be
-     *                       inflated/reinflated.
+     *                       {@link #mInflationFlags} as only those will be inflated/reinflated.
      */
     private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
         if (mRow.isRemoved()) {
@@ -249,10 +249,20 @@
         // To check if the notification has inline image and preload inline image if necessary.
         mRow.getImageResolver().preloadImages(sbn.getNotification());
 
-        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
-                mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
-                mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler);
-        if (mCallback != null && mCallback.doInflateSynchronous()) {
+        AsyncInflationTask task = new AsyncInflationTask(
+                sbn,
+                mInflateSynchronously,
+                reInflateFlags,
+                mCachedContentViews,
+                mRow,
+                mIsLowPriority,
+                mIsChildInGroup,
+                mUsesIncreasedHeight,
+                mUsesIncreasedHeadsUpHeight,
+                mRedactAmbient,
+                mCallback,
+                mRemoteViewClickHandler);
+        if (mInflateSynchronously) {
             task.onPostExecute(task.doInBackground());
         } else {
             task.execute();
@@ -260,13 +270,23 @@
     }
 
     @VisibleForTesting
-    InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags,
-            Notification.Builder builder, Context packageContext) {
+    InflationProgress inflateNotificationViews(
+            boolean inflateSynchronously,
+            @InflationFlag int reInflateFlags,
+            Notification.Builder builder,
+            Context packageContext) {
         InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
                 mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
                 mRedactAmbient, packageContext);
-        apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient,
-                mRemoteViewClickHandler, null);
+        apply(
+                inflateSynchronously,
+                result,
+                reInflateFlags,
+                mCachedContentViews,
+                mRow,
+                mRedactAmbient,
+                mRemoteViewClickHandler,
+                null);
         return result;
     }
 
@@ -349,9 +369,13 @@
         return result;
     }
 
-    public static CancellationSignal apply(InflationProgress result,
-            @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
-            ExpandableNotificationRow row, boolean redactAmbient,
+    public static CancellationSignal apply(
+            boolean inflateSynchronously,
+            InflationProgress result,
+            @InflationFlag int reInflateFlags,
+            ArrayMap<Integer, RemoteViews> cachedContentViews,
+            ExpandableNotificationRow row,
+            boolean redactAmbient,
             RemoteViews.OnClickHandler remoteViewClickHandler,
             @Nullable InflationCallback callback) {
         NotificationContentView privateLayout = row.getPrivateLayout();
@@ -374,8 +398,8 @@
                     return result.newContentView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient,
-                    isNewView, remoteViewClickHandler, callback, privateLayout,
+            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
+                    row, redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout,
                     privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
                             NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
@@ -398,9 +422,9 @@
                         return result.newExpandedView;
                     }
                 };
-                applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback,
-                        privateLayout, privateLayout.getExpandedChild(),
+                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
+                        cachedContentViews, row, redactAmbient, isNewView, remoteViewClickHandler,
+                        callback, privateLayout, privateLayout.getExpandedChild(),
                         privateLayout.getVisibleWrapper(
                                 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
                         applyCallback);
@@ -424,9 +448,9 @@
                         return result.newHeadsUpView;
                     }
                 };
-                applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback,
-                        privateLayout, privateLayout.getHeadsUpChild(),
+                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
+                        cachedContentViews, row, redactAmbient, isNewView, remoteViewClickHandler,
+                        callback, privateLayout, privateLayout.getHeadsUpChild(),
                         privateLayout.getVisibleWrapper(
                                 VISIBLE_TYPE_HEADSUP), runningInflations,
                         applyCallback);
@@ -449,8 +473,8 @@
                     return result.newPublicView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback,
+            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
+                    row, redactAmbient, isNewView, remoteViewClickHandler, callback,
                     publicLayout, publicLayout.getContractedChild(),
                     publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
@@ -473,8 +497,8 @@
                     return result.newAmbientView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback,
+            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
+                    row, redactAmbient, isNewView, remoteViewClickHandler, callback,
                     newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
                             NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
                     applyCallback);
@@ -490,18 +514,24 @@
     }
 
     @VisibleForTesting
-    static void applyRemoteView(final InflationProgress result,
-            final @InflationFlag int reInflateFlags, @InflationFlag int inflationId,
+    static void applyRemoteView(
+            boolean inflateSynchronously,
+            final InflationProgress result,
+            final @InflationFlag int reInflateFlags,
+            @InflationFlag int inflationId,
             final ArrayMap<Integer, RemoteViews> cachedContentViews,
-            final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView,
+            final ExpandableNotificationRow row,
+            final boolean redactAmbient,
+            boolean isNewView,
             RemoteViews.OnClickHandler remoteViewClickHandler,
             @Nullable final InflationCallback callback,
-            NotificationContentView parentLayout, View existingView,
+            NotificationContentView parentLayout,
+            View existingView,
             NotificationViewWrapper existingWrapper,
             final HashMap<Integer, CancellationSignal> runningInflations,
             ApplyCallback applyCallback) {
         RemoteViews newContentView = applyCallback.getRemoteView();
-        if (callback != null && callback.doInflateSynchronous()) {
+        if (inflateSynchronously) {
             try {
                 if (isNewView) {
                     View v = newContentView.apply(
@@ -724,15 +754,7 @@
          * @param entry the entry with the content views set
          * @param inflatedFlags the flags associated with the content views that were inflated
          */
-        void onAsyncInflationFinished(NotificationEntry entry,
-                @InflationFlag int inflatedFlags);
-
-        /**
-         * Used to disable async-ness for tests. Should only be used for tests.
-         */
-        default boolean doInflateSynchronous() {
-            return false;
-        }
+        void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags);
     }
 
     public void clearCachesAndReInflate() {
@@ -740,6 +762,15 @@
         inflateNotificationViews();
     }
 
+    /**
+     * Sets whether to perform inflation on the same thread as the caller. This method should only
+     * be used in tests, not in production.
+     */
+    @VisibleForTesting
+    void setInflateSynchronously(boolean inflateSynchronously) {
+        mInflateSynchronously = inflateSynchronously;
+    }
+
     private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
         NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
                 : row.getPrivateLayout();
@@ -751,6 +782,7 @@
 
         private final StatusBarNotification mSbn;
         private final Context mContext;
+        private final boolean mInflateSynchronously;
         private final boolean mIsLowPriority;
         private final boolean mIsChildInGroup;
         private final boolean mUsesIncreasedHeight;
@@ -764,14 +796,22 @@
         private RemoteViews.OnClickHandler mRemoteViewClickHandler;
         private CancellationSignal mCancellationSignal;
 
-        private AsyncInflationTask(StatusBarNotification notification,
+        private AsyncInflationTask(
+                StatusBarNotification notification,
+                boolean inflateSynchronously,
                 @InflationFlag int reInflateFlags,
-                ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
-                boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
-                boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
-                InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler) {
+                ArrayMap<Integer, RemoteViews> cachedContentViews,
+                ExpandableNotificationRow row,
+                boolean isLowPriority,
+                boolean isChildInGroup,
+                boolean usesIncreasedHeight,
+                boolean usesIncreasedHeadsUpHeight,
+                boolean redactAmbient,
+                InflationCallback callback,
+                RemoteViews.OnClickHandler remoteViewClickHandler) {
             mRow = row;
             mSbn = notification;
+            mInflateSynchronously = inflateSynchronously;
             mReInflateFlags = reInflateFlags;
             mCachedContentViews = cachedContentViews;
             mContext = mRow.getContext();
@@ -818,8 +858,8 @@
         @Override
         protected void onPostExecute(InflationProgress result) {
             if (mError == null) {
-                mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow,
-                        mRedactAmbient, mRemoteViewClickHandler, this);
+                mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
+                        mCachedContentViews, mRow, mRedactAmbient, mRemoteViewClickHandler, this);
             } else {
                 handleError(mError);
             }
@@ -867,11 +907,6 @@
             // try to purge unnecessary cached entries.
             mRow.getImageResolver().purgeCache();
         }
-
-        @Override
-        public boolean doInflateSynchronous() {
-            return mCallback != null && mCallback.doInflateSynchronous();
-        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index a5411ec..6eb376b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -52,7 +52,7 @@
     @Override
     public void preload(Uri uri) {
         PreloadImageTask newTask = new PreloadImageTask(mResolver);
-        newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+        newTask.executeOnExecutor(NotificationContentInflater.EXECUTOR, uri);
         mCache.put(uri, newTask);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4c06ff6..3808702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -26,12 +26,15 @@
 import android.graphics.Paint;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -108,6 +111,11 @@
             return false;
         }
 
+        // Apps targeting Q should fix their dark mode bugs.
+        if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) {
+            return false;
+        }
+
         int background = getBackgroundColor(view);
         if (background == Color.TRANSPARENT) {
             background = defaultBackgroundColor;
@@ -138,17 +146,19 @@
         }
     }
 
-    private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
+    @VisibleForTesting
+    boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
         if (viewGroup == null) {
             return false;
         }
 
+        int backgroundColor = getBackgroundColor(viewGroup);
+        if (Color.alpha(backgroundColor) != 255) {
+            backgroundColor = ContrastColorUtil.compositeColors(backgroundColor, parentBackground);
+            backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255);
+        }
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
             View child = viewGroup.getChildAt(i);
-            int backgroundColor = getBackgroundColor(viewGroup);
-            if (backgroundColor == Color.TRANSPARENT) {
-                backgroundColor = parentBackground;
-            }
             if (child instanceof TextView) {
                 int foreground = ((TextView) child).getCurrentTextColor();
                 if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7105876..fbf1e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -578,6 +578,10 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onDensityOrFontScaleChanged() {
+        reinflateViews();
+    }
+
+    private void reinflateViews() {
         inflateFooterView();
         inflateEmptyShadeView();
         updateFooter();
@@ -608,6 +612,7 @@
             mCornerRadius = newRadius;
             invalidate();
         }
+        reinflateViews();
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2a88080..7882fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -195,6 +195,9 @@
             return false;
         }
         ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        if (row.isBlockingHelperShowingAndTranslationFinished()) {
+            return true;
+        }
         if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
             return false;
         }
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 6410860..195d02d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -191,7 +191,7 @@
     }
 
     protected int adjustDisableFlags(int state) {
-        if (!mStatusBarComponent.isLaunchTransitionFadingAway()
+        if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
                 && !mKeyguardMonitor.isKeyguardFadingAway()
                 && shouldHideNotificationIcons()) {
             state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
index cc8adde..38df17a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -112,6 +112,9 @@
      * their icons for their buttons.
      */
     public void updateIcons() {
+        if (getCurrentView() == null || !getCurrentView().isAttachedToWindow()) {
+            return;
+        }
         for (ButtonData data : mButtonData) {
             data.button.updateIcon();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4c1c0a4..de0e194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -141,10 +141,6 @@
         mAnimationStateHandler = handler;
     }
 
-    public void destroy() {
-        Dependency.get(StatusBarStateController.class).removeCallback(this);
-    }
-
     private void initResources() {
         Resources resources = mContext.getResources();
         mStatusBarHeight = resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 5ccb9b2..b622688 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -18,14 +18,12 @@
 
 import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
 import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
-import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.util.MathUtils;
 import android.util.TimeUtils;
 
@@ -167,9 +165,7 @@
 
     public long getTintAnimationDuration() {
         if (NavBarTintController.isEnabled(mContext)) {
-            return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
-                    NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_COLOR_ADAPT_TRANSITION_TIME),
-                    MIN_COLOR_ADAPT_TRANSITION_TIME);
+            return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
         }
         return DEFAULT_TINT_ANIMATION_DURATION;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
index b4f850b..cf3f89e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.Color;
@@ -27,11 +28,13 @@
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
+import android.view.View;
+
+import com.android.systemui.R;
 
 public class NavBarTintController {
-    public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition";
     public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
-    public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1500;
+    public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
 
     private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
     private Handler mColorAdaptionHandler;
@@ -42,23 +45,25 @@
     // Passing the threshold of this luminance value will make the button black otherwise white
     private static final float LUMINANCE_THRESHOLD = 0.3f;
 
-    // The home button's icon is actually smaller than the button's size, the percentage will
-    // cut into the button's size to determine the icon size
-    private static final float PERCENTAGE_BUTTON_PADDING = 0.3f;
-
-    // The distance from the home button to color sample around
-    private static final int COLOR_SAMPLE_MARGIN = 20;
+    // The margin from the bounds of the view to color sample around
+    private static final int COLOR_SAMPLE_MARGIN = 10;
 
     private boolean mRunning;
 
     private final NavigationBarView mNavigationBarView;
     private final LightBarTransitionsController mLightBarController;
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    private final int mBarRadius;
+    private final int mBarBottom;
 
     public NavBarTintController(NavigationBarView navigationBarView,
             LightBarTransitionsController lightBarController) {
         mNavigationBarView = navigationBarView;
         mLightBarController = lightBarController;
+
+        final Resources res = navigationBarView.getResources();
+        mBarRadius = res.getDimensionPixelSize(R.dimen.navigation_handle_radius);
+        mBarBottom = res.getDimensionPixelSize(R.dimen.navigation_handle_bottom);
     }
 
     public void start() {
@@ -91,27 +96,28 @@
     private void updateTint() {
         int[] navPos = new int[2];
         int[] butPos = new int[2];
-        if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+        View view = mNavigationBarView.getHomeHandle().getCurrentView();
+        if (view == null) {
             return;
         }
 
-        // Determine the area of the home icon in the larger home button
-        mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos);
-        final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth();
-        final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight();
-        final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth);
-        final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight);
-        final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding,
-                navWidth + butPos[0]  - xPadding, navHeight + butPos[1] - yPadding);
-        if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) {
+        // Determine the area of the icon within its view bounds
+        view.getLocationInSurface(butPos);
+        final int navWidth = view.getWidth();
+        final int navHeight = view.getHeight();
+        int viewBottom = butPos[1] + navHeight - mBarBottom;
+        final Rect viewIconRect = new Rect(butPos[0], viewBottom - mBarRadius * 2,
+                butPos[0] + navWidth, viewBottom);
+
+        if (mNavigationBarView.getCurrentView() == null || viewIconRect.isEmpty()) {
             scheduleColorAdaption();
             return;
         }
         mNavigationBarView.getCurrentView().getLocationOnScreen(navPos);
-        homeButtonRect.offset(navPos[0], navPos[1]);
+        viewIconRect.offset(navPos[0], navPos[1]);
 
         // Apply a margin area around the button region to sample the colors, crop from screenshot
-        final Rect cropRect = new Rect(homeButtonRect);
+        final Rect cropRect = new Rect(viewIconRect);
         cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN);
         if (cropRect.isEmpty()) {
             scheduleColorAdaption();
@@ -120,8 +126,8 @@
 
         // Determine the size of the home area
         Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN,
-                homeButtonRect.width() + COLOR_SAMPLE_MARGIN,
-                homeButtonRect.height() + COLOR_SAMPLE_MARGIN);
+                viewIconRect.width() + COLOR_SAMPLE_MARGIN,
+                viewIconRect.height() + COLOR_SAMPLE_MARGIN);
 
         // Get the screenshot around the home button icon to determine the color
         DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -129,7 +135,8 @@
         final Bitmap hardBitmap = SurfaceControl
                 .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                         mNavigationBarView.getContext().getDisplay().getRotation());
-        if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) {
+        if (cropRect.bottom <= hardBitmap.getHeight()
+                && cropRect.left + cropRect.width() <= hardBitmap.getWidth()) {
             final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
                     cropRect.width(), cropRect.height());
             final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false);
@@ -204,6 +211,8 @@
 
     public static boolean isEnabled(Context context) {
         return Settings.Global.getInt(context.getContentResolver(),
-                NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1;
+                NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1
+            && Settings.Global.getInt(context.getContentResolver(),
+                NavigationPrototypeController.SHOW_HOME_HANDLE_SETTING, 0) == 1;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index dae4da7..64209a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -16,9 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.util.FloatProperty;
+import android.util.MathUtils;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 
@@ -27,6 +35,71 @@
 public class NavigationBarEdgePanel extends View {
     private static final String TAG = "NavigationBarEdgePanel";
 
+    // TODO: read from resources once drawing is finalized.
+    private static final boolean SHOW_PROTECTION_STROKE = true;
+    private static final int PROTECTION_COLOR = 0xffc0c0c0;
+    private static final int STROKE_COLOR = 0xffe5e5e5;
+    private static final int PROTECTION_WIDTH_PX = 4;
+    private static final int BASE_EXTENT = 32;
+    private static final int ARROW_HEIGHT_DP = 32;
+    private static final int POINT_EXTENT_DP = 8;
+    private static final int ARROW_THICKNESS_DP = 4;
+    private static final float TRACK_LENGTH_MULTIPLIER = 1.5f;
+    private static final float START_POINTING_RATIO = 0.3f;
+    private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
+    private static final int ANIM_DURATION_MS = 150;
+
+    private final Paint mPaint = new Paint();
+    private final Paint mProtectionPaint = new Paint();
+
+    private final ObjectAnimator mEndAnimator;
+    private final ObjectAnimator mLegAnimator;
+
+    private final float mDensity;
+    private final float mBaseExtent;
+    private final float mPointExtent;
+    private final float mHeight;
+    private final float mStrokeThickness;
+    private final boolean mIsLeftPanel;
+
+    private float mStartY;
+    private float mStartX;
+
+    private boolean mGestureDetected;
+    private boolean mArrowsPointLeft;
+    private float mGestureLength;
+    private float mLegProgress;
+    private float mDragProgress;
+
+    // How much the "legs" of the back arrow have proceeded from being a line to an arrow.
+    private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("legProgress") {
+        @Override
+        public void setValue(NavigationBarEdgePanel object, float value) {
+            object.setLegProgress(value);
+        }
+
+        @Override
+        public Float get(NavigationBarEdgePanel object) {
+            return object.getLegProgress();
+        }
+    };
+
+    // How far across the view the arrow should be drawn.
+    private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("dragProgress") {
+
+                @Override
+                public void setValue(NavigationBarEdgePanel object, float value) {
+                    object.setDragProgress(value);
+                }
+
+                @Override
+                public Float get(NavigationBarEdgePanel object) {
+                    return object.getDragProgress();
+                }
+            };
+
     public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
             int gravity) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
@@ -40,13 +113,43 @@
         lp.setTitle(TAG + context.getDisplayId());
         lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
         lp.windowAnimations = 0;
-        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context);
+        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
+                context, (gravity & Gravity.LEFT) == Gravity.LEFT);
         panel.setLayoutParams(lp);
         return panel;
     }
 
-    private NavigationBarEdgePanel(Context context) {
+    private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
         super(context);
+
+        mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
+        mEndAnimator.setAutoCancel(true);
+        mEndAnimator.setDuration(ANIM_DURATION_MS);
+
+        mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f);
+        mLegAnimator.setAutoCancel(true);
+        mLegAnimator.setDuration(ANIM_DURATION_MS);
+
+        mDensity = context.getResources().getDisplayMetrics().density;
+
+        mBaseExtent = dp(BASE_EXTENT);
+        mHeight = dp(ARROW_HEIGHT_DP);
+        mPointExtent = dp(POINT_EXTENT_DP);
+        mStrokeThickness = dp(ARROW_THICKNESS_DP);
+
+        mPaint.setStrokeWidth(mStrokeThickness);
+        mPaint.setStrokeCap(Paint.Cap.ROUND);
+        mPaint.setColor(STROKE_COLOR);
+        mPaint.setAntiAlias(true);
+
+        mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX);
+        mProtectionPaint.setStrokeCap(Paint.Cap.ROUND);
+        mProtectionPaint.setColor(PROTECTION_COLOR);
+        mProtectionPaint.setAntiAlias(true);
+
+        // Both panels arrow point the same way
+        mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+        mIsLeftPanel = isLeftPanel;
     }
 
     public void setWindowFlag(int flags, boolean enable) {
@@ -62,6 +165,58 @@
         updateLayout(lp);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN : {
+                show(event.getX(), event.getY());
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                handleNewSwipePoint(event.getX());
+                break;
+            }
+            // Fall through
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                hide();
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness;
+        float animatedOffset = mPointExtent * mLegProgress;
+        canvas.save();
+        canvas.translate(
+                mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
+                mStartY - mHeight * 0.5f);
+
+        float outsideX = mArrowsPointLeft ? animatedOffset : 0;
+        float middleX = mArrowsPointLeft ? 0 : animatedOffset;
+
+        if (SHOW_PROTECTION_STROKE) {
+            canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint);
+            canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint);
+        }
+
+        canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint);
+        canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint);
+        canvas.restore();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        // TODO: read the gesture length from the nav controller.
+        mGestureLength = getWidth();
+    }
+
     public void setDimensions(int width, int height) {
         final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
         if (lp.width != width || lp.height != height) {
@@ -71,8 +226,81 @@
         }
     }
 
+    private void setLegProgress(float progress) {
+        mLegProgress = progress;
+        invalidate();
+    }
+
+    private float getLegProgress() {
+        return mLegProgress;
+    }
+
+    private void setDragProgress(float dragProgress) {
+        mDragProgress = dragProgress;
+        invalidate();
+    }
+
+    private float getDragProgress() {
+        return mDragProgress;
+    }
+
+    private void hide() {
+        animate().alpha(0f).setDuration(ANIM_DURATION_MS);
+    }
+
+    private void show(float x, float y) {
+        mEndAnimator.cancel();
+        mLegAnimator.cancel();
+        setLegProgress(0f);
+        setDragProgress(0f);
+        setAlpha(1f);
+
+        float halfHeight = mHeight * 0.5f;
+        mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight);
+        mStartX = x;
+    }
+
+    private void handleNewSwipePoint(float x) {
+        float dist = MathUtils.abs(x - mStartX);
+
+        setDragProgress(MathUtils.constrainedMap(
+                0, 1.0f,
+                0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
+                dist));
+
+        if (dist < mGestureLength) {
+            float calculatedLegProgress = MathUtils.constrainedMap(
+                    0f, POINTEDNESS_BEFORE_SNAP_RATIO,
+                    mGestureLength * START_POINTING_RATIO, mGestureLength,
+                    dist);
+
+            // Blend animated value with drag calculated value, allow the gesture to continue
+            // while the animation is playing with jump cuts in the animation.
+            setLegProgress(MathUtils.lerp(calculatedLegProgress, mLegProgress, mDragProgress));
+
+            if (mGestureDetected) {
+                mGestureDetected = false;
+
+                mLegAnimator.setFloatValues(POINTEDNESS_BEFORE_SNAP_RATIO);
+                mLegAnimator.start();
+            }
+        } else {
+            if (!mGestureDetected) {
+                performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+                mGestureDetected = true;
+
+                mLegAnimator.setFloatValues(1f);
+                mLegAnimator.start();
+            }
+        }
+    }
+
     private void updateLayout(WindowManager.LayoutParams lp) {
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         wm.updateViewLayout(this, lp);
     }
+
+    private float dp(float dp) {
+        return mDensity * dp;
+    }
 }
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 538d797..1eb4990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -445,8 +445,11 @@
         // Respect the disabled flag, no need for action as flag change callback will handle hiding
         if (rotateSuggestionsDisabled) return;
 
-        mNavigationBarView.getRotateSuggestionButton()
-                .onRotationProposal(rotation, winRotation, isValid);
+        View rotationButton = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
+        if (rotationButton != null && rotationButton.isAttachedToWindow()) {
+            mNavigationBarView.getRotateSuggestionButton()
+                    .onRotationProposal(rotation, winRotation, isValid);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 7c31dae..c9fa89e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -65,6 +65,7 @@
     public static final String RECENT = "recent";
     public static final String NAVSPACE = "space";
     public static final String CLIPBOARD = "clipboard";
+    public static final String HOME_HANDLE = "home_handle";
     public static final String KEY = "key";
     public static final String LEFT = "left";
     public static final String RIGHT = "right";
@@ -393,6 +394,8 @@
             v = inflater.inflate(R.layout.clipboard, parent, false);
         } else if (CONTEXTUAL.equals(button)) {
             v = inflater.inflate(R.layout.contextual, parent, false);
+        } else if (HOME_HANDLE.equals(button)) {
+            v = inflater.inflate(R.layout.home_handle, parent, false);
         } else if (button.startsWith(KEY)) {
             String uri = extractImage(button);
             int code = extractKeycode(button);
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 651670c..d6d3d08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -29,6 +29,7 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
 
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
@@ -45,6 +46,8 @@
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -128,8 +131,8 @@
     private Rect mBackButtonBounds = new Rect();
     private Rect mRecentsButtonBounds = new Rect();
     private Rect mRotationButtonBounds = new Rect();
+    private final Region mActiveRegion = new Region();
     private int[] mTmpPosition = new int[2];
-    private Rect mTmpRect = new Rect();
 
     private KeyButtonDrawable mBackIcon;
     private KeyButtonDrawable mHomeDefaultIcon;
@@ -337,6 +340,11 @@
                 mRightEdgePanel.setDimensions(width, height);
             }
         }
+
+        @Override
+        public void onHomeHandleVisiblilityChanged(boolean visible) {
+            showHomeHandle(visible);
+        }
     };
 
     public NavigationBarView(Context context, AttributeSet attrs) {
@@ -374,6 +382,7 @@
 
         mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
         mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
+        mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
         mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
         mButtonDispatchers.put(R.id.menu, menuButton);
         mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
@@ -571,6 +580,10 @@
                 .getContextButton(R.id.rotate_suggestion);
     }
 
+    public ButtonDispatcher getHomeHandle() {
+        return mButtonDispatchers.get(R.id.home_handle);
+    }
+
     public SparseArray<ButtonDispatcher> getButtonDispatchers() {
         return mButtonDispatchers;
     }
@@ -853,6 +866,10 @@
         final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI();
 
         if (mNavigationInflaterView != null) {
+            if (mPrototypeController.showHomeHandle()) {
+                showHomeHandle(true /* visible */);
+            }
+
             // Reinflate the navbar if needed, no-op unless the swipe up state changes
             mNavigationInflaterView.onLikelyDefaultLayoutChange();
         }
@@ -897,6 +914,9 @@
 
     private void setWindowFlag(int flags, boolean enable) {
         final ViewGroup navbarView = ((ViewGroup) getParent());
+        if (navbarView == null) {
+            return;
+        }
         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams();
         if (lp == null || enable == ((lp.flags & flags) != 0)) {
             return;
@@ -910,6 +930,18 @@
         wm.updateViewLayout(navbarView, lp);
     }
 
+    private void showHomeHandle(boolean visible) {
+        mNavigationInflaterView.onTuningChanged(NAV_BAR_VIEWS,
+                visible ? getContext().getString(R.string.config_navBarLayoutHandle) : null);
+
+        // Color adaption is tied with showing home handle, only avaliable if visible
+        if (visible) {
+            mColorAdaptionController.start();
+        } else {
+            mColorAdaptionController.end();
+        }
+    }
+
     public void setMenuVisibility(final boolean show) {
         mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
     }
@@ -954,17 +986,22 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds);
-        updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
-        updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds);
-        updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds);
+
+        mActiveRegion.setEmpty();
+        updateButtonLocation(getBackButton(), mBackButtonBounds, true);
+        updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
+        updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
+        updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
+        // TODO: Handle button visibility changes
+        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
         if (mGestureHelper != null) {
             mGestureHelper.onLayout(changed, left, top, right, bottom);
         }
         mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
-    private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
+    private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
+            boolean isActive) {
         View view = button.getCurrentView();
         if (view == null) {
             buttonBounds.setEmpty();
@@ -975,6 +1012,14 @@
         final float posY = view.getTranslationY();
         view.setTranslationX(0);
         view.setTranslationY(0);
+
+        if (isActive) {
+            view.getLocationOnScreen(mTmpPosition);
+            buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
+                    mTmpPosition[0] + view.getMeasuredWidth(),
+                    mTmpPosition[1] + view.getMeasuredHeight());
+            mActiveRegion.op(buttonBounds, Op.UNION);
+        }
         view.getLocationInWindow(mTmpPosition);
         buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
                 mTmpPosition[0] + view.getMeasuredWidth(),
@@ -1121,6 +1166,12 @@
             // If car mode or density changes, we need to reset the icons.
             updateNavButtonIcons();
         }
+
+        if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
+            mColorAdaptionController.start();
+        } else {
+            mColorAdaptionController.end();
+        }
     }
 
     /**
@@ -1212,10 +1263,11 @@
                     .getSystemService(Context.WINDOW_SERVICE);
             int width = mPrototypeController.getEdgeSensitivityWidth();
             int height = mPrototypeController.getEdgeSensitivityHeight();
+            // Explicitly left and right, not start and end as this is device relative.
             mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.START | Gravity.BOTTOM);
+                    Gravity.LEFT | Gravity.BOTTOM);
             mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.END | Gravity.BOTTOM);
+                    Gravity.RIGHT | Gravity.BOTTOM);
             mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
@@ -1237,15 +1289,12 @@
             mButtonDispatchers.valueAt(i).onDestroy();
         }
 
-        if (mPrototypeController.isEnabled()) {
-            WindowManager wm = (WindowManager) getContext()
-                    .getSystemService(Context.WINDOW_SERVICE);
-            if (mLeftEdgePanel != null) {
-                wm.removeView(mLeftEdgePanel);
-            }
-            if (mRightEdgePanel != null) {
-                wm.removeView(mRightEdgePanel);
-            }
+        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        if (mLeftEdgePanel != null) {
+            wm.removeView(mLeftEdgePanel);
+        }
+        if (mRightEdgePanel != null) {
+            wm.removeView(mRightEdgePanel);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
new file mode 100644
index 0000000..968074c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 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.animation.ArgbEvaluator;
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+
+public class NavigationHandle extends View implements ButtonInterface {
+    private float mDarkIntensity = -1;
+
+    private final Paint mPaint = new Paint();
+    private @ColorInt final int mLightColor;
+    private @ColorInt final int mDarkColor;
+    private final int mRadius;
+    private final int mBottom;
+
+    public NavigationHandle(Context context) {
+        this(context, null);
+    }
+
+    public NavigationHandle(Context context, AttributeSet attr) {
+        super(context, attr);
+        final Resources res = context.getResources();
+        mRadius = res.getDimensionPixelSize(R.dimen.navigation_handle_radius);
+        mBottom = res.getDimensionPixelSize(R.dimen.navigation_handle_bottom);
+
+        final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
+        final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
+        Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme);
+        Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme);
+        mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor);
+        mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
+        mPaint.setAntiAlias(true);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        // Draw that bar
+        int navHeight = getHeight();
+        int height = mRadius * 2;
+        int width = getWidth();
+        int y = (navHeight - mBottom - height);
+        canvas.drawRoundRect(mRadius, y, width - mRadius, y + height, mRadius, mRadius, mPaint);
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+    }
+
+    @Override
+    public void abortCurrentGesture() {
+    }
+
+    @Override
+    public void setVertical(boolean vertical) {
+    }
+
+    @Override
+    public void setDarkIntensity(float intensity) {
+        if (mDarkIntensity != intensity) {
+            mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(intensity, mLightColor,
+                    mDarkColor));
+            mDarkIntensity = intensity;
+            invalidate();
+        }
+    }
+
+    @Override
+    public void setDelayTouchFeedback(boolean shouldDelay) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index 8421e23..2c31e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Point;
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
@@ -37,12 +38,11 @@
     private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
     private static final String PROTOTYPE_ENABLED = "prototype_enabled";
 
-    private static final String EDGE_SENSITIVITY_HEIGHT_SETTING =
-            "quickstepcontroller_edge_height_sensitivity";
     public static final String EDGE_SENSITIVITY_WIDTH_SETTING =
             "quickstepcontroller_edge_width_sensitivity";
     private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
     public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
+    public static final String SHOW_HOME_HANDLE_SETTING = "quickstepcontroller_showhandle";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
@@ -86,7 +86,7 @@
         registerObserver(GESTURE_MATCH_SETTING);
         registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
         registerObserver(EDGE_SENSITIVITY_WIDTH_SETTING);
-        registerObserver(EDGE_SENSITIVITY_HEIGHT_SETTING);
+        registerObserver(SHOW_HOME_HANDLE_SETTING);
     }
 
     /**
@@ -114,20 +114,29 @@
             } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
                 mListener.onColorAdaptChanged(
                         NavBarTintController.isEnabled(mContext));
-            } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING)
-                    || path.endsWith(EDGE_SENSITIVITY_HEIGHT_SETTING)) {
+            } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING)) {
                 mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
                         getEdgeSensitivityHeight());
+            } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
+                mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
             }
         }
     }
 
+    /**
+     * @return the width for edge swipe
+     */
     public int getEdgeSensitivityWidth() {
         return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_WIDTH_SETTING, 0));
     }
 
+    /**
+     * @return full screen height
+     */
     public int getEdgeSensitivityHeight() {
-        return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_HEIGHT_SETTING, 0));
+        final Point size = new Point();
+        mContext.getDisplay().getRealSize(size);
+        return size.y;
     }
 
     public boolean isEnabled() {
@@ -149,6 +158,10 @@
         return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
     }
 
+    boolean showHomeHandle() {
+        return getGlobalBool(SHOW_HOME_HANDLE_SETTING, false /* default */);
+    }
+
     /**
      * Since Settings.Global cannot pass arrays, use a string to represent each character as a
      * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
@@ -185,6 +198,7 @@
         void onGestureRemap(@GestureAction int[] actions);
         void onBackButtonVisibilityChanged(boolean visible);
         void onHomeButtonVisibilityChanged(boolean visible);
+        void onHomeHandleVisiblilityChanged(boolean visible);
         void onColorAdaptChanged(boolean enabled);
         void onEdgeSensitivityChanged(int width, int height);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index b613e8e..4dbd854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
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 62f85fe..99269cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -47,6 +48,7 @@
     private final NotificationEntryManager mEntryManager;
     private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
     private final StatusBarStateController mStatusBarStateController;
+    private final NotificationMediaManager mMediaManager;
     @VisibleForTesting
     final NotificationListener.NotificationSettingsListener mSettingsListener =
             new NotificationListener.NotificationSettingsListener() {
@@ -93,13 +95,15 @@
 
     public NotificationIconAreaController(Context context, StatusBar statusBar,
             StatusBarStateController statusBarStateController,
-            NotificationListener notificationListener) {
+            NotificationListener notificationListener,
+            NotificationMediaManager notificationMediaManager) {
         mStatusBar = statusBar;
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mEntryManager = Dependency.get(NotificationEntryManager.class);
         mStatusBarStateController = statusBarStateController;
         mStatusBarStateController.addCallback(this);
+        mMediaManager = notificationMediaManager;
         notificationListener.addNotificationSettingsListener(mSettingsListener);
 
         initializeNotificationAreaViews(context);
@@ -192,10 +196,13 @@
 
     protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
-            boolean hideRepliedMessages) {
+            boolean hideRepliedMessages, boolean hideCurrentMedia) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
+        if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) {
+            return false;
+        }
         if (!showLowPriority && !entry.isHighPriority()) {
             return false;
         }
@@ -235,14 +242,16 @@
     private void updateShelfIcons() {
         updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
                 true /* showAmbient */, !mFullyDark /* showLowPriority */,
-                false /* hideDismissed */, mFullyDark /* hideRepliedMessages */);
+                false /* hideDismissed */, mFullyDark /* hideRepliedMessages */,
+                mFullyDark /* hideCurrentMedia */);
     }
 
     public void updateStatusBarIcons() {
         updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
                 false /* showAmbient */, mShowLowPriority /* showLowPriority */,
                 true /* hideDismissed */,
-                true /* hideRepliedMessages */);
+                true /* hideRepliedMessages */,
+                false /* hideCurrentMedia */);
     }
 
     @VisibleForTesting
@@ -261,7 +270,7 @@
      */
     private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
-            boolean hideDismissed, boolean hideRepliedMessages) {
+            boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -271,7 +280,7 @@
             if (view instanceof ExpandableNotificationRow) {
                 NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
-                        hideRepliedMessages)) {
+                        hideRepliedMessages, hideCurrentMedia)) {
                     toShow.add(function.apply(ent));
                 }
             }
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 069703e..f4fa1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -590,11 +590,6 @@
                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
                     mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
-            // Move big clock up while pulling up the bouncer
-            PropertyAnimator.setProperty(mBigClockContainer, AnimatableProperty.Y,
-                    MathUtils.lerp(-mBigClockContainer.getHeight(), 0,
-                          Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(getExpandedFraction())),
-                    CLOCK_ANIMATION_PROPERTIES, animateClock);
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
@@ -1334,8 +1329,7 @@
         }
     };
 
-    private void setKeyguardBottomAreaVisibility(int statusBarState,
-            boolean goingToFullShade) {
+    private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
         mKeyguardBottomArea.animate().cancel();
         if (goingToFullShade) {
             mKeyguardBottomArea.animate()
@@ -1438,6 +1432,7 @@
         if (mBarState == StatusBarState.SHADE_LOCKED
                 || mBarState == StatusBarState.KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
+            updateBigClockAlpha();
         }
         if (mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled) {
@@ -1883,6 +1878,19 @@
         }
     }
 
+    /**
+     * Custom clock fades away when user drags up to unlock or pulls down quick settings.
+     *
+     * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
+     * {@link updateKeyguardBottomAreaAlpha}.
+     */
+    private void updateBigClockAlpha() {
+        float expansionAlpha = MathUtils.map(isUnlockHintRunning()
+                ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction());
+        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+        mBigClockContainer.setAlpha(alpha);
+    }
+
     private float getNotificationsTopY() {
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
             return getExpandedHeight();
@@ -2597,6 +2605,7 @@
         }
         mNotificationStackScroller.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
+        updateBigClockAlpha();
         updateStatusBarIcons();
     }
 
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 18711c0..e0c5e59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -92,6 +92,8 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.NotificationChannels;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.List;
 import java.util.Locale;
 
@@ -284,25 +286,6 @@
         });
     }
 
-    public void destroy() {
-        mRotationLockController.removeCallback(this);
-        mBluetooth.removeCallback(this);
-        mProvisionedController.removeCallback(this);
-        mZenController.removeCallback(this);
-        mCast.removeCallback(mCastCallback);
-        mHotspot.removeCallback(mHotspotCallback);
-        mNextAlarmController.removeCallback(mNextAlarmCallback);
-        mDataSaver.removeCallback(this);
-        mKeyguardMonitor.removeCallback(this);
-        mPrivacyItemController.removeCallback(this);
-        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
-        mContext.unregisterReceiver(mIntentReceiver);
-
-        NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
-        mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
-                new UserHandle(v.second)));
-    }
-
     @Override
     public void onZenChanged(int zen) {
         updateVolumeZen();
@@ -812,6 +795,15 @@
         boolean showMicrophone = false;
         boolean showLocation = false;
         for (PrivacyItem item : items) {
+            if (item == null /* b/124234367 */) {
+                if (DEBUG) {
+                    Log.e(TAG, "updatePrivacyItems - null item found");
+                    StringWriter out = new StringWriter();
+                    mPrivacyItemController.dump(null, new PrintWriter(out), null);
+                    Log.e(TAG, out.toString());
+                }
+                continue;
+            }
             switch (item.getPrivacyType()) {
                 case TYPE_CAMERA:
                     showCamera = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 84f1cef..73ab527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -56,6 +56,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
 import com.android.systemui.shared.system.NavigationBarCompat;
 
 import java.io.PrintWriter;
@@ -676,8 +677,13 @@
     }
 
     private boolean proxyMotionEvents(MotionEvent event) {
-        final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         event.transform(mTransformGlobalMatrix);
+        InputEventDispatcher dispatcher = mOverviewEventSender.getInputEventDispatcher();
+        if (dispatcher != null) {
+            dispatcher.dispatch(event);
+        }
+
+        final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         try {
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                 overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
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 9f3bec6..51ffc1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -117,7 +117,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.DateTimeView;
-import android.widget.ImageView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
@@ -189,7 +188,6 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -461,9 +459,6 @@
     protected boolean mDozing;
     private boolean mDozingRequested;
 
-    protected BackDropView mBackdrop;
-    protected ImageView mBackdropFront, mBackdropBack;
-
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
@@ -484,8 +479,7 @@
             updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
             // If WallpaperInfo is null, it must be ImageWallpaper.
             final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && (info == null && aodImageWallpaperEnabled
-                        || info != null && info.supportsAmbientMode());
+                    && (info == null || info.supportsAmbientMode());
 
             mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -493,7 +487,6 @@
     };
 
     private Runnable mLaunchTransitionEndRunnable;
-    protected boolean mLaunchTransitionFadingAway;
     private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
@@ -653,7 +646,7 @@
 
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this,
-                StatusBarStateControllerImpl.RANK_STATUS_BAR);
+                SysuiStatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -933,11 +926,9 @@
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
         mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
 
-        mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
-        mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
-        mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
-        mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack,
-                mScrimController, mLockscreenWallpaper);
+        BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+        mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+                backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1550,12 +1541,12 @@
 
     @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        mEntryManager.updateNotificationRanking(null /* rankingMap */);
+        mEntryManager.updateNotifications();
     }
 
     @Override
     public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
-        mEntryManager.updateNotificationRanking(null);
+        mEntryManager.updateNotifications();
         if (isAmbient) {
             mDozeServiceHost.fireNotificationPulse();
         } else if (!mAmbientPulseManager.hasNotifications()) {
@@ -1593,10 +1584,6 @@
         return mPulsing;
     }
 
-    public boolean isLaunchTransitionFadingAway() {
-        return mLaunchTransitionFadingAway;
-    }
-
     public boolean hideStatusBarIconsWhenExpanded() {
         return mNotificationPanel.hideStatusBarIconsWhenExpanded();
     }
@@ -1893,6 +1880,8 @@
 
             mStatusBarWindow.cancelExpandHelper();
             mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+        } else {
+            mBubbleController.collapseStack();
         }
     }
 
@@ -2533,6 +2522,9 @@
                 if (mRemoteInputManager.getController() != null) {
                     mRemoteInputManager.getController().closeRemoteInputs();
                 }
+                if (mBubbleController.isStackExpanded()) {
+                    mBubbleController.collapseStack();
+                }
                 if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
@@ -2546,6 +2538,9 @@
                 if (mStatusBarWindowController != null) {
                     mStatusBarWindowController.setNotTouchable(false);
                 }
+                if (mBubbleController.isStackExpanded()) {
+                    mBubbleController.collapseStack();
+                }
                 finishBarAnimations();
                 resetUserExpandedStates();
             }
@@ -2843,38 +2838,6 @@
         startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
     }
 
-    public void destroy() {
-        // Begin old BaseStatusBar.destroy().
-        mContext.unregisterReceiver(mBannerActionBroadcastReceiver);
-        mLockscreenUserManager.destroy();
-        try {
-            mNotificationListener.unregisterAsSystemService();
-        } catch (RemoteException e) {
-            // Ignore.
-        }
-        mNotificationListController.destroy();
-        // End old BaseStatusBar.destroy().
-        if (mStatusBarWindow != null) {
-            mWindowManager.removeViewImmediate(mStatusBarWindow);
-            mStatusBarWindow = null;
-        }
-        mNavigationBarController.destroy();
-        mContext.unregisterReceiver(mBroadcastReceiver);
-        mContext.unregisterReceiver(mDemoReceiver);
-        mAssistManager.destroy();
-        mHeadsUpManager.destroy();
-        mStatusBarStateController.removeCallback(this);
-
-        if (mQSPanel != null && mQSPanel.getHost() != null) {
-            mQSPanel.getHost().destroy();
-        }
-        Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
-        mDeviceProvisionedController.removeCallback(mUserSetupObserver);
-        Dependency.get(ConfigurationController.class).removeCallback(this);
-        mZenController.removeCallback(this);
-        mAppOpsController.removeCallback(APP_OPS, this);
-    }
-
     private boolean mDemoModeAllowed;
     private boolean mDemoMode;
 
@@ -2999,7 +2962,7 @@
 
     public void showKeyguardImpl() {
         mIsKeyguard = true;
-        if (mLaunchTransitionFadingAway) {
+        if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
             mNotificationPanel.animate().cancel();
             onLaunchTransitionFadingEnded();
         }
@@ -3031,7 +2994,7 @@
         mNotificationPanel.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
-        mLaunchTransitionFadingAway = false;
+        mKeyguardMonitor.setLaunchTransitionFadingAway(false);
         mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
     }
 
@@ -3057,7 +3020,6 @@
         mLaunchTransitionEndRunnable = endRunnable;
         Runnable hideRunnable = () -> {
             mKeyguardMonitor.setLaunchTransitionFadingAway(true);
-            mLaunchTransitionFadingAway = true;
             if (beforeFading != null) {
                 beforeFading.run();
             }
@@ -3299,7 +3261,11 @@
             return true;
         }
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
-            animateCollapsePanels();
+            if (mNotificationPanel.canPanelBeCollapsed()) {
+                animateCollapsePanels();
+            } else {
+                mBubbleController.performBackPressIfNeeded();
+            }
             return true;
         }
         if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) {
@@ -4036,8 +4002,7 @@
                 float viewY = screenY - mTmpInt2[1];
                 if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
                         && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
-                    if (mAmbientIndicationContainer instanceof DozeReceiver)
-                    ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap();
+                    dispatchTap(mAmbientIndicationContainer, viewX, viewY);
                 }
             }
         }
@@ -4052,6 +4017,12 @@
             mScrimController.setAodFrontScrimAlpha(scrimOpacity);
         }
 
+        private void dispatchTap(View view, float x, float y) {
+            long now = SystemClock.elapsedRealtime();
+            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
+            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
+        }
+
         private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
             MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
             view.dispatchTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index e9705ff..3ce66c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -258,6 +258,11 @@
         }
     }
 
+    @Override
+    public void onOverlayChanged() {
+        onDensityOrFontScaleChanged();
+    }
+
     private void updateNotificationOnUiModeChanged() {
         ArrayList<NotificationEntry> userNotifications
                 = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
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 6495910..6e36c01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A container for Status bar system icons. Limits the number of system icons and handles overflow
@@ -67,6 +68,8 @@
     private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
     // So we can count and measure properly
     private ArrayList<View> mMeasureViews = new ArrayList<>();
+    // Any ignored icon will never be added as a child
+    private ArrayList<String> mIgnoredSlots = new ArrayList<>();
 
     public StatusIconContainer(Context context) {
         this(context, null);
@@ -146,7 +149,8 @@
         // Collect all of the views which want to be laid out
         for (int i = 0; i < count; i++) {
             StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i);
-            if (icon.isIconVisible() && !icon.isIconBlocked()) {
+            if (icon.isIconVisible() && !icon.isIconBlocked()
+                    && !mIgnoredSlots.contains(icon.getSlot())) {
                 mMeasureViews.add((View) icon);
             }
         }
@@ -205,6 +209,47 @@
     }
 
     /**
+     * Add a name of an icon slot to be ignored. It will not show up nor be measured
+     * @param slotName name of the icon as it exists in
+     * frameworks/base/core/res/res/values/config.xml
+     */
+    public void addIgnoredSlot(String slotName) {
+        addIgnoredSlotInternal(slotName);
+        requestLayout();
+    }
+
+    /**
+     * Add a list of slots to be ignored
+     * @param slots names of the icons to ignore
+     */
+    public void addIgnoredSlots(List<String> slots) {
+        for (String slot : slots) {
+            addIgnoredSlotInternal(slot);
+        }
+
+        requestLayout();
+    }
+
+    private void addIgnoredSlotInternal(String slotName) {
+        if (!mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.add(slotName);
+        }
+    }
+
+    /**
+     * Remove a slot from the list of ignored icon slots. It will then be shown when set to visible
+     * by the {@link StatusBarIconController}.
+     * @param slotName name of the icon slot to remove from the ignored list
+     */
+    public void removeIgnoredSlot(String slotName) {
+        if (mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.remove(slotName);
+        }
+
+        requestLayout();
+    }
+
+    /**
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
@@ -223,7 +268,8 @@
             StatusIconDisplayable iconView = (StatusIconDisplayable) child;
             StatusIconState childState = getViewStateFromChild(child);
 
-            if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
+            if (!iconView.isIconVisible() || iconView.isIconBlocked()
+                    || mIgnoredSlots.contains(iconView.getSlot())) {
                 childState.visibleState = STATE_HIDDEN;
                 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fd3f680..0461057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,7 +31,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
index 6ee341d..f446cef 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -156,6 +156,8 @@
 
     private boolean checkIfNeedMask() {
         // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
+        // Because of conflicting with another wallpaper feature,
+        // we only support LockScreen wallpaper currently.
         return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
     }
 
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e73c70b..efb4ff0 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -68,6 +68,13 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name="com.android.systemui.bubbles.BubblesTestActivity"
+            android:allowEmbedded="true"
+            android:documentLaunchMode="always"
+            android:excludeFromRecents="true"
+            android:exported="false"
+            android:resizeableActivity="true" />
+
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 77895c9..190ce75 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -75,6 +75,17 @@
     }
 
     @Test
+    public void hasHeader_readsSliceData() {
+        ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+        builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
+    }
+
+    @Test
     public void refresh_replacesSliceContentAndNotifiesListener() {
         AtomicBoolean notified = new AtomicBoolean();
         mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
new file mode 100644
index 0000000..58701e7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.util.InjectionInflationController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockManagerTest extends SysuiTestCase {
+
+    private static final String BUBBLE_CLOCK = BubbleClockController.class.getName();
+    private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class;
+
+    private ClockManager mClockManager;
+    private ContentObserver mContentObserver;
+    private DockManagerFake mFakeDockManager;
+    @Mock InjectionInflationController mMockInjectionInflationController;
+    @Mock SysuiColorExtractor mMockColorExtractor;
+    @Mock ContentResolver mMockContentResolver;
+    @Mock SettingsWrapper mMockSettingsWrapper;
+    @Mock ClockManager.ClockChangedListener mMockListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        LayoutInflater inflater = LayoutInflater.from(getContext());
+        when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
+
+        mFakeDockManager = new DockManagerFake();
+        mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
+                mFakeDockManager, mMockColorExtractor, mMockContentResolver, mMockSettingsWrapper);
+
+        mClockManager.addOnClockChangedListener(mMockListener);
+        mContentObserver = mClockManager.getContentObserver();
+    }
+
+    @After
+    public void tearDown() {
+        mClockManager.removeOnClockChangedListener(mMockListener);
+    }
+
+    @Test
+    public void dockEvent() {
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        assertThat(mClockManager.isDocked()).isTrue();
+    }
+
+    @Test
+    public void undockEvent() {
+        mFakeDockManager.setDockEvent(DockManager.STATE_NONE);
+        assertThat(mClockManager.isDocked()).isFalse();
+    }
+
+    @Test
+    public void getCurrentClock_default() {
+        // GIVEN that settings doesn't contain any values
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null);
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null);
+        // WHEN settings change event is fired
+        mContentObserver.onChange(false);
+        // THEN the result is null, indicated the default clock face should be used.
+        assertThat(mClockManager.getCurrentClock()).isNull();
+    }
+
+    @Test
+    public void getCurrentClock_customClock() {
+        // GIVEN that settings is set to the bubble clock face
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN settings change event is fired
+        mContentObserver.onChange(false);
+        // THEN the plugin is the bubble clock face.
+        assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+
+    @Test
+    public void getCurrentClock_badSettingsValue() {
+        // GIVEN that settings contains a value that doesn't correspond to a
+        // custom clock face.
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value");
+        // WHEN settings change event is fired
+        mContentObserver.onChange(false);
+        // THEN the result is null.
+        assertThat(mClockManager.getCurrentClock()).isNull();
+    }
+
+    @Test
+    public void getCurrentClock_dockedDefault() {
+        // WHEN dock event is fired
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        // THEN the result is null, indicating the default clock face.
+        assertThat(mClockManager.getCurrentClock()).isNull();
+    }
+
+    @Test
+    public void getCurrentClock_dockedCustomClock() {
+        // GIVEN settings is set to the bubble clock face
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN dock event fires
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        // THEN the plugin is the bubble clock face.
+        assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+
+    @Test
+    public void getCurrentClock_badDockedSettingsValue() {
+        // GIVEN settings contains a value that doesn't correspond to an available clock face.
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+        // WHEN dock event fires
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        // THEN the result is null.
+        assertThat(mClockManager.getCurrentClock()).isNull();
+    }
+
+    @Test
+    public void getCurrentClock_badDockedSettingsFallback() {
+        // GIVEN settings contains a value that doesn't correspond to an available clock face, but
+        // locked screen settings is set to bubble clock.
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN dock event is fired
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        // THEN the plugin is the bubble clock face.
+        assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2742577..ca72602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -82,6 +82,8 @@
     @Mock
     private BubbleController.BubbleExpandListener mBubbleExpandListener;
 
+    private BubbleData mBubbleData;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -104,7 +106,9 @@
         when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
         when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
 
-        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+        mBubbleData = new BubbleData();
+        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
+                mBubbleData);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
@@ -207,12 +211,12 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow2.getEntry().showInShadeWhenBubble());
 
         // Switch which bubble is expanded
         stackView.setExpandedBubble(mRow.getEntry());
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow.getEntry().showInShadeWhenBubble());
 
         // collapse for previous bubble
@@ -262,19 +266,19 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow2.getEntry().showInShadeWhenBubble());
 
         // Dismiss currently expanded
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
 
         // Make sure next bubble is selected
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
 
         // Dismiss that one
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
 
         // Make sure state changes and collapse happens
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
@@ -297,8 +301,8 @@
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
-                StatusBarWindowController statusBarWindowController) {
-            super(context, statusBarWindowController);
+                StatusBarWindowController statusBarWindowController, BubbleData data) {
+            super(context, statusBarWindowController, data);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
new file mode 100644
index 0000000..ea472da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.systemui.R;
+
+/**
+ * Referenced by NotificationTestHelper#makeBubbleMetadata
+ */
+public class BubblesTestActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index c0aac7e..3bd582f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -22,6 +22,8 @@
 import android.graphics.PointF;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 
@@ -63,15 +65,13 @@
     public void testExpansionAndCollapse() throws InterruptedException {
         Runnable afterExpand = Mockito.mock(Runnable.class);
         mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
         Mockito.verify(afterExpand).run();
 
         Runnable afterCollapse = Mockito.mock(Runnable.class);
         mExpandedController.collapseBackToStack(afterCollapse);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
         testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
@@ -79,17 +79,70 @@
     }
 
     @Test
-    public void testOnChildRemoved() throws InterruptedException {
-        Runnable afterExpand = Mockito.mock(Runnable.class);
-        mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
+    public void testOnChildAdded() throws InterruptedException {
+        expand();
+
+        // Add another new view and wait for its animation.
+        final View newView = new FrameLayout(getContext());
+        mLayout.addView(newView, 0);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testOnChildRemoved() throws InterruptedException {
+        expand();
 
         // Remove some views and see if the remaining child views still pass the expansion test.
         mLayout.removeView(mViews.get(0));
         mLayout.removeView(mViews.get(3));
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f);
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDismissed() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f);
+        mLayout.removeView(draggedBubble);
+        waitForLayoutMessageQueue();
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        assertEquals(-1, mLayout.indexOfChild(draggedBubble));
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    /** Expand the stack and wait for animations to finish. */
+    private void expand() throws InterruptedException {
+        mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class));
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
     }
 
     /** Check that children are in the correct positions for being stacked. */
@@ -108,7 +161,7 @@
     }
 
     /** Check that children are in the correct positions for being expanded. */
-    private void testExpanded() {
+    private void testBubblesInCorrectExpandedPositions() {
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) {
             assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 31e44d7..d94b669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -155,6 +155,11 @@
         }
 
         @Override
+        public void cancelAnimationsOnView(View view) {
+            mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
+        }
+
+        @Override
         protected void animateValueForChildAtIndex(DynamicAnimation.ViewProperty property,
                 int index, float value, float startVel, Runnable after) {
             mMainThreadHandler.post(() ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 98bf3c27..bb384dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.Handler
@@ -28,11 +29,18 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER
+import com.android.systemui.Dependency.MAIN_HANDLER
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -81,15 +89,20 @@
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var privacyItemController: PrivacyItemController
+    private lateinit var handler: Handler
+
+    fun PrivacyItemController(context: Context) =
+            PrivacyItemController(context, appOpsController, handler, handler)
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
+        handler = Handler(testableLooper.looper)
 
         appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+        mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
         mContext.addMockSystemService(UserManager::class.java, userManager)
         mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
                 DEVICE_SERVICES_STRING)
@@ -232,4 +245,26 @@
         verify(callback, never()).privacyChanged(anyList())
         verify(otherCallback).privacyChanged(anyList())
     }
+
+    @Test
+    fun testListShouldNotHaveNull() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+                        AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        privacyItemController.addCallback(callback)
+        testableLooper.processAllMessages()
+
+        verify(callback).privacyChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+        assertThat(argCaptor.value, not(hasItem(nullValue())))
+    }
+
+    @Test
+    fun testListShouldBeCopy() {
+        val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+                PrivacyApplication("", TEST_UID, mContext)))
+        privacyItemController.privacyList = list
+        assertEquals(list, privacyItemController.privacyList)
+        assertTrue(list !== privacyItemController.privacyList)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 660f853..7b1253a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
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 0b24c21..3c919a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -36,10 +36,11 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblesTestActivity;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -278,7 +279,7 @@
         entry.channel.setBlockableSystem(true);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
-        NotificationInflaterTest.runThenWaitForInflation(
+        NotificationContentInflaterTest.runThenWaitForInflation(
                 () -> row.inflateViews(),
                 row.getNotificationInflater());
 
@@ -290,7 +291,8 @@
     }
 
     private Notification.BubbleMetadata makeBubbleMetadata() {
-        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Intent target = new Intent(mContext, BubblesTestActivity.class);
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
         return new Notification.BubbleMetadata.Builder()
                 .setIntent(bubbleIntent)
                 .setTitle("bubble title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 79bc0a3..04e7cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,8 +68,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -143,7 +143,7 @@
 
         @Override
         public void onAsyncInflationFinished(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+                @InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
             mCountDownLatch.countDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 6d35539..f6fb416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,9 +18,9 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 648df3c..99dc9f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -64,9 +65,9 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class NotificationInflaterTest extends SysuiTestCase {
+public class NotificationContentInflaterTest extends SysuiTestCase {
 
-    private NotificationInflater mNotificationInflater;
+    private NotificationContentInflater mNotificationInflater;
     private Notification.Builder mBuilder;
     private ExpandableNotificationRow mRow;
 
@@ -80,8 +81,8 @@
         ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
                 mBuilder.build());
         mRow = spy(row);
-        mNotificationInflater = new NotificationInflater(mRow);
-        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        mNotificationInflater = new NotificationContentInflater(mRow);
+        mNotificationInflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -89,7 +90,7 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
             }
         });
     }
@@ -98,7 +99,11 @@
     public void testIncreasedHeadsUpBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(
+                false /* inflateSynchronously */,
+                FLAG_CONTENT_VIEW_ALL,
+                builder,
+                mContext);
         verify(builder).createHeadsUpContentView(true);
     }
 
@@ -106,7 +111,11 @@
     public void testIncreasedHeightBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(
+                false /* inflateSynchronously */,
+                FLAG_CONTENT_VIEW_ALL,
+                builder,
+                mContext);
         verify(builder).createContentView(true);
     }
 
@@ -158,14 +167,18 @@
     @Test
     @Ignore
     public void testInflationIsRetriedIfAsyncFails() throws Exception {
-        NotificationInflater.InflationProgress result =
-                new NotificationInflater.InflationProgress();
+        NotificationContentInflater.InflationProgress result =
+                new NotificationContentInflater.InflationProgress();
         result.packageContext = mContext;
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+        NotificationContentInflater.applyRemoteView(
+                false /* inflateSynchronously */,
+                result,
+                FLAG_CONTENT_VIEW_EXPANDED,
+                0,
                 new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
                 true /* isNewView */, (v, p, r) -> true,
-                new NotificationInflater.InflationCallback() {
+                new InflationCallback() {
                     @Override
                     public void handleInflationException(StatusBarNotification notification,
                             Exception e) {
@@ -175,11 +188,11 @@
 
                     @Override
                     public void onAsyncInflationFinished(NotificationEntry entry,
-                            @NotificationInflater.InflationFlag int inflatedFlags) {
+                            @NotificationContentInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
                 }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
-                new NotificationInflater.ApplyCallback() {
+                new NotificationContentInflater.ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
                     }
@@ -199,8 +212,8 @@
         mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
         mNotificationInflater.updateNeedsRedaction(true);
 
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
         assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC,
                 asyncInflationTask.getReInflateFlags());
         asyncInflationTask.abort();
@@ -217,8 +230,8 @@
         mNotificationInflater.setIsChildInGroup(true);
 
         InflationTask runningTask = mRow.getEntry().getRunningTask();
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) runningTask;
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) runningTask;
         assertEquals("Successive inflations don't inherit the previous flags!",
                 FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
         runningTask.abort();
@@ -233,19 +246,20 @@
                 R.layout.custom_view_dark));
         RemoteViews decoratedMediaView = mBuilder.createContentView();
         Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
-                NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+                NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
     }
 
     public static void runThenWaitForInflation(Runnable block,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         runThenWaitForInflation(block, false /* expectingException */, inflater);
     }
 
     private static void runThenWaitForInflation(Runnable block, boolean expectingException,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
         final ExceptionHolder exceptionHolder = new ExceptionHolder();
-        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        inflater.setInflateSynchronously(true);
+        inflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -257,18 +271,13 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
                             "Inflation finished even though there should be an error"));
                 }
                 countDownLatch.countDown();
             }
-
-            @Override
-            public boolean doInflateSynchronous() {
-                return true;
-            }
         });
         block.run();
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 24aa772..637b30c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static org.mockito.Mockito.mock;
 
 import android.content.Context;
 import android.support.test.filters.SmallTest;
@@ -22,13 +24,15 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.util.Assert;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,12 +41,26 @@
 @RunWithLooper
 public class NotificationViewWrapperTest extends SysuiTestCase {
 
-    @Test
-    public void constructor_doesntUseViewContext() throws Exception {
+    private View mView;
+    private ExpandableNotificationRow mRow;
+    private TestableNotificationViewWrapper mNotificationViewWrapper;
+
+    @Before
+    public void setup() throws Exception {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        new TestableNotificationViewWrapper(mContext,
-                new View(mContext),
-                new NotificationTestHelper(getContext()).createRow());
+        mView = mock(View.class);
+        mRow = new NotificationTestHelper(getContext()).createRow();
+        mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
+    }
+
+    @Test
+    public void childrenNeedInversion_doesntCrash_whenOpacity() {
+        LinearLayout viewGroup = new LinearLayout(mContext);
+        TextView textView = new TextView(mContext);
+        textView.setTextColor(0xcc000000);
+        viewGroup.addView(textView);
+
+        mNotificationViewWrapper.childrenNeedInversion(0xcc000000, viewGroup);
     }
 
     static class TestableNotificationViewWrapper extends NotificationViewWrapper {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
index 6177344..5c80718 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -24,9 +24,11 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
 import android.media.ImageReader;
+import android.os.SystemClock;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
 
@@ -39,12 +41,15 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.function.Predicate;
+
 /** atest NavigationBarButtonTest */
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
 public class NavigationBarButtonTest extends SysuiTestCase {
 
+    private static final String TAG = "NavigationBarButtonTest";
     private ImageReader mReader;
     private NavigationBarView mNavBar;
     private VirtualDisplay mVirtualDisplay;
@@ -78,9 +83,31 @@
 
         assertNotNull("virtual display must not be null", mVirtualDisplay);
 
+        waitForDisplayReady(mVirtualDisplay.getDisplay().getDisplayId());
+
         return mVirtualDisplay.getDisplay();
     }
 
+    private void waitForDisplayReady(int displayId) {
+        waitForDisplayCondition(displayId, state -> state);
+    }
+
+    private void waitForDisplayCondition(int displayId, Predicate<Boolean> condition) {
+        for (int retry = 1; retry <= 10; retry++) {
+            if (condition.test(isDisplayOn(displayId))) {
+                return;
+            }
+            Log.i(TAG, "Waiting for virtual display ready ... retry = " + retry);
+            SystemClock.sleep(500);
+        }
+    }
+
+    private boolean isDisplayOn(int displayId) {
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        Display display = displayManager.getDisplay(displayId);
+        return display != null && display.getState() == Display.STATE_ON;
+    }
+
     @After
     public void tearDown() {
         releaseDisplay();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 608dd8b..120d0b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -29,6 +29,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationMediaManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,6 +48,8 @@
     StatusBar mStatusBar;
     @Mock
     StatusBarStateController mStatusBarStateController;
+    @Mock
+    private NotificationMediaManager mMediaManager;
     private NotificationIconAreaController mController;
 
     @Before
@@ -54,7 +57,7 @@
         MockitoAnnotations.initMocks(this);
 
         mController = new NotificationIconAreaController(mContext, mStatusBar,
-                mStatusBarStateController, mListener);
+                mStatusBarStateController, mListener, mMediaManager);
     }
 
     @Test
diff --git a/packages/VpnDialogs/Android.bp b/packages/VpnDialogs/Android.bp
new file mode 100644
index 0000000..6f2f50c
--- /dev/null
+++ b/packages/VpnDialogs/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2011 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.
+//
+
+android_app {
+    name: "VpnDialogs",
+    certificate: "platform",
+    privileged: true,
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+}
diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk
deleted file mode 100644
index 8507646..0000000
--- a/packages/VpnDialogs/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Copyright (C) 2011 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := VpnDialogs
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp
new file mode 100644
index 0000000..1bec492
--- /dev/null
+++ b/packages/WAPPushManager/Android.bp
@@ -0,0 +1,12 @@
+// Copyright 2007-2008 The Android Open Source Project
+
+android_app {
+    name: "WAPPushManager",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    libs: ["telephony-common"],
+    static_libs: ["android-common"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+}
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
deleted file mode 100644
index 91526dd..0000000
--- a/packages/WAPPushManager/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2007-2008 The Android Open Source Project
-
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := WAPPushManager
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_JAVA_LIBRARIES += telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES += android-common
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-# This finds and builds the test apk as well, so a single make does both.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/WAPPushManager/tests/Android.bp b/packages/WAPPushManager/tests/Android.bp
new file mode 100644
index 0000000..25c6121
--- /dev/null
+++ b/packages/WAPPushManager/tests/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2008, 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.
+
+android_test {
+    name: "WAPPushManagerTests",
+    libs: [
+        "android.test.runner",
+        "telephony-common",
+        "android.test.base",
+    ],
+    static_libs: ["junit"],
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+        "src/com/android/smspush/unitTests/IDataVerify.aidl",
+    ],
+    // Notice that we don't have to include the src files of Email
+    // because running the tests using an instrumentation targeting
+    // Email, we automatically get all of its classes loaded into
+    // our environment.
+    platform_apis: true,
+    instrumentation_for: "WAPPushManager",
+}
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
deleted file mode 100644
index c4c2240f..0000000
--- a/packages/WAPPushManager/tests/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2008, 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += \
-        src/com/android/smspush/unitTests/IDataVerify.aidl
-
-
-# Notice that we don't have to include the src files of Email because, by
-# running the tests using an instrumentation targeting Eamil, we
-# automatically get all of its classes loaded into our environment.
-
-LOCAL_PACKAGE_NAME := WAPPushManagerTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_INSTRUMENTATION_FOR := WAPPushManager
-
-include $(BUILD_PACKAGE)
-
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
new file mode 100644
index 0000000..56020cd
--- /dev/null
+++ b/packages/WallpaperBackup/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+    name: "WallpaperBackup",
+    srcs: ["src/**/*.java"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+    platform_apis: true,
+    certificate: "platform",
+    privileged: false,
+}
diff --git a/packages/WallpaperBackup/Android.mk b/packages/WallpaperBackup/Android.mk
deleted file mode 100644
index a6426a6..0000000
--- a/packages/WallpaperBackup/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_PACKAGE_NAME := WallpaperBackup
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := false
-
-include $(BUILD_PACKAGE)
-
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 4254a0b..2e40c1a 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -115,8 +115,10 @@
             // We always back up this 'empty' file to ensure that the absence of
             // storable wallpaper imagery still produces a non-empty backup data
             // stream, otherwise it'd simply be ignored in preflight.
-            FileOutputStream touch = new FileOutputStream(empty);
-            touch.close();
+            if (!empty.exists()) {
+                FileOutputStream touch = new FileOutputStream(empty);
+                touch.close();
+            }
             fullBackupFile(empty, data);
 
             SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
diff --git a/packages/WallpaperCropper/Android.bp b/packages/WallpaperCropper/Android.bp
new file mode 100644
index 0000000..40c4235
--- /dev/null
+++ b/packages/WallpaperCropper/Android.bp
@@ -0,0 +1,11 @@
+android_app {
+    name: "WallpaperCropper",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    product_specific: true,
+    privileged: true,
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+}
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
deleted file mode 100644
index 2fa1dde..0000000
--- a/packages/WallpaperCropper/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := WallpaperCropper
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRODUCT_MODULE := true
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index 1294dbb..7ce13c2 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -44,6 +44,7 @@
 LOCAL_MODULE := frameworks-base-overlays-debug
 LOCAL_REQUIRED_MODULES := \
 	ExperimentNavigationBarFloatingOverlay \
+	ExperimentNavigationBarVisualInsetOverlay \
 	ExperimentNavigationBarDefaultOverlay \
 	ExperimentNavigationBarSlimOverlay32 \
 	ExperimentNavigationBarSlimOverlay40 \
diff --git a/packages/FusedLocation/Android.mk b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/Android.mk
similarity index 66%
rename from packages/FusedLocation/Android.mk
rename to packages/overlays/ExperimentNavigationBarVisualInsetOverlay/Android.mk
index d795870..56bf516 100644
--- a/packages/FusedLocation/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/Android.mk
@@ -1,29 +1,30 @@
-# Copyright (C) 2012 The Android Open Source Project
+#
+#  Copyright 2018, The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # 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.
+#
 
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_RRO_THEME := ExperimentNavigationBarVisualInset
+LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_JAVA_LIBRARIES := com.android.location.provider
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := FusedLocation
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarVisualInsetOverlay
+LOCAL_SDK_VERSION := current
 
-include $(BUILD_PACKAGE)
+include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..3ea5dc4
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.internal.experiment.navbar.type.inset"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="com.android.internal.experiment_navbar_visual_inset"
+        android:priority="1"/>
+
+    <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
+</manifest>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/config.xml
new file mode 100644
index 0000000..d35a744
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/config.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.
+ */
+-->
+<resources>
+    <!-- Height of the bottom navigation / system bar. -->
+    <dimen name="navigation_bar_height">16dp</dimen>
+    <!-- Width of the navigation bar when it is placed vertically on the screen -->
+    <dimen name="navigation_bar_width">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml
new file mode 100644
index 0000000..84dfcb7
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Name of overlay [CHAR LIMIT=64] -->
+    <string name="experiment_navigationbar_overlay" translatable="false">Visual Inset Navigation Bar</string>
+</resources>
\ No newline at end of file
diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp
new file mode 100644
index 0000000..93b2d95
--- /dev/null
+++ b/packages/services/PacProcessor/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2010 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.
+//
+
+android_app {
+    name: "PacProcessor",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    jni_libs: ["libjni_pacprocessor"],
+}
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
deleted file mode 100644
index be9ba43..0000000
--- a/packages/services/PacProcessor/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := PacProcessor
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
-
-include $(BUILD_PACKAGE)
diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp
new file mode 100644
index 0000000..87aa763
--- /dev/null
+++ b/packages/services/Proxy/Android.bp
@@ -0,0 +1,7 @@
+android_app {
+    name: "ProxyHandler",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/services/Proxy/Android.mk b/packages/services/Proxy/Android.mk
deleted file mode 100644
index ce1715f..0000000
--- a/packages/services/Proxy/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ProxyHandler
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3c91069..d9adec8 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6975,6 +6975,23 @@
     // formerly: histogram system_cost_for_smart_sharing
     FIELD_TIME_TO_APP_TARGETS = 1653;
 
+    // Open: Settings > Panel for Internet Connectivity
+    PANEL_INTERNET_CONNECTIVITY = 1654;
+
+    // Open: Settings > Panel for Volume
+    PANEL_VOLUME = 1655;
+
+    // Open: Settings > Panel for NFC
+    PANEL_NFC = 1656;
+
+    // Open: Settings > Panel for Media Output
+    PANEL_MEDIA_OUTPUT = 1657;
+
+    // ACTION: An interaction with a Slice or other component in the Panel.
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_PANEL_INTERACTION = 1658;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 9b863a9..61f63d3 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -47,7 +47,7 @@
 
     private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
     RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
             int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -106,6 +106,12 @@
                 activityComponent, focusedId, focusedValue));
     }
 
+    @Override
+    public String toString() {
+        return "RemoteAugmentedAutofillService["
+                + ComponentName.flattenToShortString(getComponentName()) + "]";
+    }
+
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
@@ -181,11 +187,13 @@
 
         @Override
         protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
-            Slog.wtf(TAG, "timed out: " + this);
+            // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the
+            // timeout
+            Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS
+                    + "ms) for " + remoteService);
             // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
             finish();
         }
-
     }
 
     public interface RemoteAugmentedAutofillServiceCallbacks
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 17e9b35..00cb6d3 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -47,6 +47,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
+import com.android.server.backup.utils.FileUtils;
+import com.android.server.backup.utils.RandomAccessFileUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -89,6 +91,12 @@
      */
     private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
 
+    /**
+     * Name of file for non-system users that remembers whether backup was explicitly activated or
+     * deactivated with a call to setBackupServiceActive.
+     */
+    private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated";
+
     // Product-level suppression of backup/restore.
     private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
 
@@ -134,11 +142,17 @@
     }
 
     /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getActivatedFileForNonSystemUser(int userId) {
-        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
-                BACKUP_ACTIVATED_FILENAME + "-" + userId);
+    protected File getRememberActivatedFileForNonSystemUser(int userId) {
+        return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir(
+                REMEMBER_ACTIVATED_FILENAME_PREFIX, userId));
     }
 
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
+    }
+
+    // TODO (b/124359804) move to util method in FileUtils
     private void createFile(File file) throws IOException {
         if (file.exists()) {
             return;
@@ -150,6 +164,7 @@
         }
     }
 
+    // TODO (b/124359804) move to util method in FileUtils
     private void deleteFile(File file) {
         if (!file.exists()) {
             return;
@@ -312,6 +327,19 @@
     public void setBackupServiceActive(int userId, boolean makeActive) {
         enforcePermissionsOnUser(userId);
 
+        // In Q, backup is OFF by default for non-system users. In the future, we will change that
+        // to ON unless backup was explicitly deactivated with a (permissioned) call to
+        // setBackupServiceActive.
+        // Therefore, remember this for use in the future. Basically the default in the future will
+        // be: rememberFile.exists() ? rememberFile.value() : ON
+        // Note that this has to be done right after the permission checks and before any other
+        // action since we need to remember that a permissioned call was made irrespective of
+        // whether the call changes the state or not.
+        if (userId != UserHandle.USER_SYSTEM) {
+            RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId),
+                    makeActive);
+        }
+
         if (mGlobalDisable) {
             Slog.i(TAG, "Backup service not supported");
             return;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index aabd41a..4638ac6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -48,4 +48,9 @@
         // is a staging dir, we dont need to copy below dir to new system user dir
         return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
     }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    static File getStateFileInSystemDir(String prefix, int userId) {
+        return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b2afbc3..32e2cac 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -70,6 +70,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -130,6 +131,7 @@
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.FileUtils;
 import com.android.server.backup.utils.SparseArrayUtils;
 
 import com.google.android.collect.Sets;
@@ -1483,19 +1485,50 @@
     }
 
     /**
-     * Clear an application's data, blocking until the operation completes or times out. If {@code
-     * keepSystemState} is {@code true}, we intentionally do not clear system state that would
-     * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
-     * bringing it into the actual expected state related to the already-restored notification state
-     * etc.
+     * Clear an application's data after a failed restore, blocking until the operation completes or
+     * times out.
      */
-    public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
-        // Don't wipe packages marked allowClearUserData=false
+    public void clearApplicationDataAfterRestoreFailure(String packageName) {
+        clearApplicationDataSynchronous(packageName, true, false);
+    }
+
+    /**
+     * Clear an application's data before restore, blocking until the operation completes or times
+     * out.
+     */
+    public void clearApplicationDataBeforeRestore(String packageName) {
+        clearApplicationDataSynchronous(packageName, false, true);
+    }
+
+    /**
+     * Clear an application's data, blocking until the operation completes or times out.
+     *
+     * @param checkFlagAllowClearUserDataOnFailedRestore if {@code true} uses
+     *    {@link ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE} to decide if
+     *    clearing data is allowed after a failed restore.
+     *
+     * @param keepSystemState if {@code true}, we don't clear system state such as already restored
+     *    notification settings, permission grants, etc.
+     */
+    private void clearApplicationDataSynchronous(String packageName,
+            boolean checkFlagAllowClearUserDataOnFailedRestore, boolean keepSystemState) {
         try {
-            PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
-            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+            ApplicationInfo applicationInfo = mPackageManager.getPackageInfoAsUser(
+                    packageName, 0, mUserId).applicationInfo;
+
+            boolean shouldClearData;
+            if (checkFlagAllowClearUserDataOnFailedRestore
+                    && applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+                shouldClearData = (applicationInfo.privateFlags
+                    & ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) != 0;
+            } else {
+                shouldClearData =
+                    (applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) != 0;
+            }
+
+            if (!shouldClearData) {
                 if (MORE_DEBUG) {
-                    Slog.i(TAG, "allowClearUserData=false so not wiping "
+                    Slog.i(TAG, "Clearing app data is not allowed so not wiping "
                             + packageName);
                 }
                 return;
@@ -1510,8 +1543,8 @@
         synchronized (mClearDataLock) {
             mClearingData = true;
             try {
-                mActivityManager.clearApplicationUserData(
-                        packageName, keepSystemState, observer, mUserId);
+                mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer,
+                        mUserId);
             } catch (RemoteException e) {
                 // can't happen because the activity manager is in this process
             }
@@ -2319,6 +2352,7 @@
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                 "setAncestralSerialNumber");
         Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+        // TODO (b/124359804)
         try (RandomAccessFile af = getAncestralSerialNumberFile()) {
             af.writeLong(ancestralSerialNumber);
         } catch (IOException e) {
@@ -2331,6 +2365,7 @@
      * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
      */
     public long getAncestralSerialNumber() {
+        // TODO (b/124359804)
         try (RandomAccessFile af = getAncestralSerialNumberFile()) {
             return af.readLong();
         } catch (IOException e) {
@@ -2344,13 +2379,7 @@
             mAncestralSerialNumberFile = new File(
                 UserBackupManagerFiles.getBaseStateDir(getUserId()),
                 SERIAL_ID_FILE);
-            if (!mAncestralSerialNumberFile.exists()) {
-                try {
-                    mAncestralSerialNumberFile.createNewFile();
-                } catch (IOException e) {
-                    Slog.w(TAG, "serial number mapping file creation failed", e);
-                }
-            }
+            FileUtils.createNewFile(mAncestralSerialNumberFile);
         }
         return new RandomAccessFile(mAncestralSerialNumberFile, "rwd");
     }
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 c5389fa..836a5e8 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -352,7 +352,7 @@
                                         Slog.d(TAG,
                                                 "Clearing app data preparatory to full restore");
                                     }
-                                    mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
+                                    mBackupManagerService.clearApplicationDataBeforeRestore(pkg);
                                 } else {
                                     if (MORE_DEBUG) {
                                         Slog.d(TAG, "backup agent ("
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 324c2d9..6714b0a 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -660,7 +660,7 @@
             ++mCount;
         } catch (Exception e) {
             Slog.e(TAG, "Error when attempting restore: " + e.toString());
-            keyValueAgentErrorCleanup();
+            keyValueAgentErrorCleanup(false);
             executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
         }
     }
@@ -686,6 +686,7 @@
         boolean staging = !packageName.equals("android");
         ParcelFileDescriptor stage;
         File downloadFile = (staging) ? mStageName : mBackupDataName;
+        boolean startedAgentRestore = false;
 
         try {
             IBackupTransport transport =
@@ -766,13 +767,15 @@
             long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
                     mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
+            startedAgentRestore = true;
             mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                     mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
         } catch (Exception e) {
             Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                     packageName, e.toString());
-            keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
+            // Clears any pending timeout messages as well.
+            keyValueAgentErrorCleanup(startedAgentRestore);
 
             // After a restore failure we go back to running the queue.  If there
             // are no more packages to be restored that will be handled by the
@@ -832,7 +835,7 @@
             Slog.e(TAG, "Unable to finalize restore of " + packageName);
             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                     packageName, e.toString());
-            keyValueAgentErrorCleanup();
+            keyValueAgentErrorCleanup(true);
             executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
         }
     }
@@ -988,8 +991,8 @@
 
                     // We also need to wipe the current target's data, as it's probably
                     // in an incoherent state.
-                    backupManagerService.clearApplicationDataSynchronous(
-                            mCurrentPackage.packageName, false);
+                    backupManagerService.clearApplicationDataAfterRestoreFailure(
+                            mCurrentPackage.packageName);
 
                     // Schedule the next state based on the nature of our failure
                     if (status == BackupTransport.TRANSPORT_ERROR) {
@@ -1110,11 +1113,18 @@
         mListener.onFinished(callerLogString);
     }
 
-    void keyValueAgentErrorCleanup() {
-        // If the agent fails restore, it might have put the app's data
-        // into an incoherent state.  For consistency we wipe its data
-        // again in this case before continuing with normal teardown
-        backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName, false);
+    /**
+     * @param clearAppData - set to {@code true} if the backup agent had already been invoked when
+     *     restore faied. So the app data may be in corrupted state and has to be cleared.
+     */
+    void keyValueAgentErrorCleanup(boolean clearAppData) {
+        if (clearAppData) {
+            // If the agent fails restore, it might have put the app's data
+            // into an incoherent state.  For consistency we wipe its data
+            // again in this case before continuing with normal teardown
+            backupManagerService.clearApplicationDataAfterRestoreFailure(
+                    mCurrentPackage.packageName);
+        }
         keyValueAgentCleanup();
     }
 
@@ -1251,7 +1261,7 @@
                 // Some kind of horrible semantic error; we're in an unexpected state.
                 // Back off hard and wind up.
                 Slog.e(TAG, "Unexpected restore callback into state " + mState);
-                keyValueAgentErrorCleanup();
+                keyValueAgentErrorCleanup(true);
                 nextState = UnifiedRestoreState.FINAL;
                 break;
             }
@@ -1271,7 +1281,7 @@
         EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                 mCurrentPackage.packageName, "restore timeout");
         // Handle like an agent that threw on invocation: wipe it and go on to the next
-        keyValueAgentErrorCleanup();
+        keyValueAgentErrorCleanup(true);
         executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
     }
 
diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java
new file mode 100644
index 0000000..00686cb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/** Utility methods useful for working with backup related files. */
+public final class FileUtils {
+    /**
+     * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored.
+     * This method is useful to avoid code duplication of the "try-catch-ignore exception" block.
+     */
+    public static File createNewFile(File file) {
+        try {
+            file.createNewFile();
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e);
+        }
+        return file;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
new file mode 100644
index 0000000..abf906a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/** Utility methods useful for working with backup related RandomAccessFiles. */
+public final class RandomAccessFileUtils {
+    private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException {
+        return new RandomAccessFile(file, "rwd");
+    }
+
+    /** Write a boolean to a File by wrapping it using a RandomAccessFile. */
+    public static void writeBoolean(File file, boolean b) {
+        try (RandomAccessFile af = getRandomAccessFile(file)) {
+            af.writeBoolean(b);
+        } catch (IOException e) {
+            Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e);
+        }
+    }
+
+    /** Read a boolean from a File by wrapping it using a RandomAccessFile. */
+    public static boolean readBoolean(File file, boolean def) {
+        try (RandomAccessFile af = getRandomAccessFile(file)) {
+            return af.readBoolean();
+        } catch (IOException e) {
+            Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e);
+        }
+        return def;
+    }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 4afbc64..45ceeda 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -45,6 +45,7 @@
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.view.contentcapture.ContentCaptureHelper;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.IContentCaptureManager;
 import android.view.contentcapture.UserDataRemovalRequest;
@@ -79,7 +80,8 @@
 
     private final LocalService mLocalService = new LocalService();
 
-    private final LocalLog mRequestsHistory = new LocalLog(20);
+    @Nullable
+    final LocalLog mRequestsHistory;
 
     @GuardedBy("mLock")
     private ActivityManagerInternal mAm;
@@ -105,15 +107,19 @@
                 UserManager.DISALLOW_CONTENT_CAPTURE);
         DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ActivityThread.currentApplication().getMainExecutor(),
-                (namespace, key, value) -> {
-                    if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED
-                            .equals(key)) {
-                        Slog.i(mTag, "Ignoring change on " + key);
-                        return;
-                    }
-                    setDisabledByDeviceConfig(value);
-                });
-        setDisabledByDeviceConfig();
+                (namespace, key, value) -> onDeviceConfigChange(key, value));
+        setLoggingLevelFromDeviceConfig();
+        setDisabledFromDeviceConfig();
+
+        final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty(
+                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
+        if (loggingSize > 0) {
+            if (debug) Slog.d(mTag, "log history size: " + loggingSize);
+            mRequestsHistory = new LocalLog(loggingSize);
+        } else {
+            if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize);
+            mRequestsHistory = null;
+        }
 
         // Sets which services are disabled
         final UserManager um = getContext().getSystemService(UserManager.class);
@@ -213,7 +219,33 @@
         return false;
     }
 
-    private void setDisabledByDeviceConfig() {
+    private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
+        switch (key) {
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED:
+                setDisabledByDeviceConfig(value);
+                return;
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL:
+                setLoggingLevelFromDeviceConfig();
+                return;
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
+                // TODO(b/123096662): implement it
+                Slog.d(mTag, "changes on " + key + " not supported yet");
+                return;
+            default:
+                Slog.i(mTag, "Ignoring change on " + key);
+        }
+    }
+
+    private void setLoggingLevelFromDeviceConfig() {
+        ContentCaptureHelper.setLoggingLevel();
+        verbose = ContentCaptureHelper.sVerbose;
+        debug = ContentCaptureHelper.sDebug;
+    }
+
+    private void setDisabledFromDeviceConfig() {
         final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
         setDisabledByDeviceConfig(value);
@@ -327,13 +359,6 @@
         }
     }
 
-    /**
-     * Logs a request so it's dumped later...
-     */
-    void logRequestLocked(@NonNull String historyItem) {
-        mRequestsHistory.log(historyItem);
-    }
-
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -480,31 +505,6 @@
         }
 
         @Override
-        public void setContentCaptureFeatureEnabled(boolean enabled,
-                @NonNull IResultReceiver result) {
-            final int userId = UserHandle.getCallingUserId();
-            final boolean isService;
-            synchronized (mLock) {
-                isService = assertCalledByServiceLocked("setContentCaptureFeatureEnabled()", userId,
-                        Binder.getCallingUid(), result);
-            }
-            if (!isService) return;
-
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Settings.Secure.putStringForUser(getContext().getContentResolver(),
-                        Settings.Secure.CONTENT_CAPTURE_ENABLED, Boolean.toString(enabled), userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            try {
-                result.send(ContentCaptureManager.RESULT_CODE_TRUE, /* resultData= */null);
-            } catch (RemoteException e) {
-                Slog.w(mTag, "Unable to send setContentCaptureFeatureEnabled(): " + e);
-            }
-        }
-
-        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return;
 
@@ -527,9 +527,13 @@
             synchronized (mLock) {
                 dumpLocked("", pw);
             }
-            if (showHistory) {
-                pw.println(); pw.println("Requests history:"); pw.println();
+            pw.print("Requests history: ");
+            if (mRequestsHistory == null) {
+                pw.println("disabled by device config");
+            } else if (showHistory) {
+                pw.println();
                 mRequestsHistory.reverseDump(fd, pw, args);
+                pw.println();
             }
         }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 7102b82..9e15960 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -21,6 +21,7 @@
 import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
 import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR;
 import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
+import static android.view.contentcapture.ContentCaptureSession.STATE_PACKAGE_NOT_WHITELISTED;
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
@@ -43,11 +44,12 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
-import android.util.Log;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.contentcapture.UserDataRemovalRequest;
 
@@ -87,6 +89,12 @@
     private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback =
             new ContentCaptureServiceRemoteCallback();
 
+    /**
+     * List of packages that are whitelisted to be content captured.
+     */
+    @GuardedBy("mLock")
+    private final ArraySet<String> mWhitelistedPackages = new ArraySet<>();
+
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
     ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
@@ -185,15 +193,19 @@
         final int taskId = activityPresentationInfo.taskId;
         final int displayId = activityPresentationInfo.displayId;
         final ComponentName componentName = activityPresentationInfo.componentName;
+        final boolean whitelisted = isWhitelistedLocked(componentName);
         final ComponentName serviceComponentName = getServiceComponentName();
         final boolean enabled = isEnabledLocked();
-        final String historyItem =
-                "id=" + sessionId + " uid=" + uid
-                + " a=" + ComponentName.flattenToShortString(componentName)
-                + " t=" + taskId + " d=" + displayId
-                + " s=" + ComponentName.flattenToShortString(serviceComponentName)
-                + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
-        mMaster.logRequestLocked(historyItem);
+        if (mMaster.mRequestsHistory != null) {
+            final String historyItem =
+                    "id=" + sessionId + " uid=" + uid
+                    + " a=" + ComponentName.flattenToShortString(componentName)
+                    + " t=" + taskId + " d=" + displayId
+                    + " s=" + ComponentName.flattenToShortString(serviceComponentName)
+                    + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)")
+                    + " w=" + whitelisted;
+            mMaster.mRequestsHistory.log(historyItem);
+        }
 
         if (!enabled) {
             // TODO: it would be better to split in differet reasons, like
@@ -212,6 +224,16 @@
             return;
         }
 
+        if (!whitelisted) {
+            if (mMaster.debug) {
+                Slog.d(TAG, "startSession(" + componentName + "): not whitelisted");
+            }
+            // TODO(b/122595322): need to return STATE_ACTIVITY_NOT_WHITELISTED as well
+            setClientState(clientReceiver, STATE_DISABLED | STATE_PACKAGE_NOT_WHITELISTED,
+                    /* binder= */ null);
+            return;
+        }
+
         final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
         if (existingSession != null) {
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
@@ -245,6 +267,26 @@
         newSession.notifySessionStartedLocked(clientReceiver);
     }
 
+    @GuardedBy("mLock")
+    private boolean isWhitelistedLocked(@NonNull ComponentName componentName) {
+        // TODO(b/122595322): need to check whitelisted activities as well.
+        final String packageName = componentName.getPackageName();
+        return mWhitelistedPackages.contains(packageName);
+    }
+
+    private void whitelistPackages(@NonNull List<String> packages) {
+        // TODO(b/122595322): add CTS test for when it's null
+        synchronized (mLock) {
+            if (packages == null) {
+                if (mMaster.verbose) Slog.v(TAG, "clearing all whitelisted packages");
+                mWhitelistedPackages.clear();
+            } else {
+                if (mMaster.verbose) Slog.v(TAG, "whitelisting packages: " + packages);
+                mWhitelistedPackages.addAll(packages);
+            }
+        }
+    }
+
     // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
     public void finishSessionLocked(@NonNull String sessionId) {
@@ -376,15 +418,23 @@
             mRemoteService.dump(prefix2, pw);
         }
 
+        final int whitelistSize = mWhitelistedPackages.size();
+        pw.print(prefix); pw.print("Whitelisted packages: "); pw.println(whitelistSize);
+        for (int i = 0; i < whitelistSize; i++) {
+            final String whitelistedPkg = mWhitelistedPackages.valueAt(i);
+            pw.print(prefix2); pw.print(i + 1); pw.print(": "); pw.println(whitelistedPkg);
+        }
+
         if (mSessions.isEmpty()) {
             pw.print(prefix); pw.println("no sessions");
         } else {
-            final int size = mSessions.size();
-            pw.print(prefix); pw.print("number sessions: "); pw.println(size);
-            for (int i = 0; i < size; i++) {
-                pw.print(prefix); pw.print("session@"); pw.println(i);
+            final int sessionsSize = mSessions.size();
+            pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize);
+            for (int i = 0; i < sessionsSize; i++) {
+                pw.print(prefix); pw.print("#"); pw.println(i);
                 final ContentCaptureServerSession session = mSessions.valueAt(i);
                 session.dumpLocked(prefix2, pw);
+                pw.println();
             }
         }
     }
@@ -410,11 +460,26 @@
         public void setContentCaptureWhitelist(List<String> packages,
                 List<ComponentName> activities) {
             if (mMaster.verbose) {
-                Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities="
+                Slog.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities="
                         + activities + ")");
             }
-            // TODO(b/122595322): implement
+            whitelistPackages(packages);
+
+            // TODO(b/122595322): whitelist activities as well
             // TODO(b/119613670): log metrics
         }
+
+        @Override
+        public void disableSelf() {
+            if (mMaster.verbose) Slog.v(TAG, "disableSelf()");
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Settings.Secure.putStringForUser(getContext().getContentResolver(),
+                        Settings.Secure.CONTENT_CAPTURE_ENABLED, "false", mUserId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3c52e17..4094843 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -20,6 +20,7 @@
 import android.os.IBinder;
 import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.SnapshotData;
+import android.util.LocalLog;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureSessionId;
@@ -37,6 +38,9 @@
     final IBinder mActivityToken;
     private final ContentCapturePerUserService mService;
     private final RemoteContentCaptureService mRemoteService;
+
+    // NOTE: this is the "internal" context (like package and taskId), not the explicit content
+    // set by apps - those are only send to the ContentCaptureService.
     private final ContentCaptureContext mContentCaptureContext;
 
     /**
@@ -83,7 +87,10 @@
      */
     @GuardedBy("mLock")
     public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
-        mService.getMaster().logRequestLocked("snapshot: id=" + mId);
+        final LocalLog logHistory = mService.getMaster().mRequestsHistory;
+        if (logHistory != null) {
+            logHistory.log("snapshot: id=" + mId);
+        }
 
         mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d1cd072..915c131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.CaptivePortal;
 import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
@@ -86,6 +88,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
@@ -917,7 +920,8 @@
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
-        //set up the listener for user state for creating user VPNs
+        // Set up the listener for user state for creating user VPNs.
+        // Should run on mHandler to avoid any races.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_STARTED);
         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -925,7 +929,11 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
@@ -936,7 +944,11 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
 
         try {
             mNMS.registerObserver(mTethering);
@@ -2690,11 +2702,6 @@
                     EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
                     mNai.network.netId));
         }
-
-        @Override
-        public void logCaptivePortalLoginEvent(int eventId, String packageName) {
-            new MetricsLogger().action(eventId, packageName);
-        }
     }
 
     private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -2842,6 +2849,8 @@
         if (DBG) {
             log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
         }
+        // Clear all notifications of this network.
+        mNotifier.clearNotification(nai.network.netId);
         // A network agent has disconnected.
         // TODO - if we move the logic to the network agent (have them disconnect
         // because they lost all their requests or because their score isn't good)
@@ -3247,22 +3256,63 @@
     /**
      * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
      * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Bundle to use as intent extras for the captive portal application.
      *                  Must be treated as opaque to avoid preventing the captive portal app to
      *                  update its arguments.
      */
     @Override
-    public void startCaptivePortalAppInternal(Bundle appExtras) {
+    public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
         mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
 
         final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
         appIntent.putExtras(appExtras);
+        appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+                new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
 
+    private class CaptivePortalImpl extends ICaptivePortal.Stub {
+        private final Network mNetwork;
+
+        private CaptivePortalImpl(Network network) {
+            mNetwork = network;
+        }
+
+        @Override
+        public void appResponse(final int response) throws RemoteException {
+            if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+                enforceSettingsPermission();
+            }
+
+            // getNetworkAgentInfoForNetwork is thread-safe
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+            if (nai == null) return;
+
+            // nai.networkMonitor() is thread-safe
+            final INetworkMonitor nm = nai.networkMonitor();
+            if (nm == null) return;
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                nm.notifyCaptivePortalAppFinished(response);
+            } finally {
+                // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void logEvent(int eventId, String packageName) {
+            enforceSettingsPermission();
+
+            new MetricsLogger().action(eventId, packageName);
+        }
+    }
+
     public boolean avoidBadWifi() {
         return mMultinetworkPolicyTracker.getAvoidBadWifi();
     }
@@ -4097,17 +4147,27 @@
      * handler thread through their agent, this is asynchronous. When the capabilities objects
      * are computed they will be up-to-date as they are computed synchronously from here and
      * this is running on the ConnectivityService thread.
-     * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
      */
     private void updateAllVpnsCapabilities() {
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             for (int i = 0; i < mVpns.size(); i++) {
                 final Vpn vpn = mVpns.valueAt(i);
-                vpn.updateCapabilities();
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
+    private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+        ensureRunningOnConnectivityServiceThread();
+        NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+        if (vpnNai == null || nc == null) {
+            return;
+        }
+        updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+    }
+
     @Override
     public boolean updateLockdownVpn() {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4448,22 +4508,28 @@
 
     private void onUserAdded(int userId) {
         mPermissionMonitor.onUserAdded(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserAdded(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
     private void onUserRemoved(int userId) {
         mPermissionMonitor.onUserRemoved(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserRemoved(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
@@ -4532,6 +4598,7 @@
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            ensureRunningOnConnectivityServiceThread();
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5041,6 +5108,19 @@
         return getNetworkForRequest(mDefaultRequest.requestId);
     }
 
+    @Nullable
+    private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+        return nai != null ? nai.network : null;
+    }
+
+    private void ensureRunningOnConnectivityServiceThread() {
+        if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on ConnectivityService thread: "
+                            + Thread.currentThread().getName());
+        }
+    }
+
     private boolean isDefaultNetwork(NetworkAgentInfo nai) {
         return nai == getDefaultNetwork();
     }
@@ -5097,7 +5177,7 @@
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+            getNetworkStack().makeNetworkMonitor(
                     toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -5109,6 +5189,11 @@
         return nai.network.netId;
     }
 
+    @VisibleForTesting
+    protected NetworkStackClient getNetworkStack() {
+        return NetworkStackClient.getInstance();
+    }
+
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
         nai.onNetworkMonitorCreated(networkMonitor);
         if (VDBG) log("Got NetworkAgent Messenger");
@@ -5667,6 +5752,8 @@
         updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
+        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+        updateAllVpnsCapabilities();
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6106,6 +6193,10 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
+            if (networkAgent.isVPN()) {
+                updateAllVpnsCapabilities();
+            }
+
             // Consider network even though it is not yet validated.
             final long now = SystemClock.elapsedRealtime();
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6367,7 +6458,11 @@
             success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
         if (success) {
-            mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+            mHandler.post(() -> {
+                // Update VPN's capabilities based on updated underlying network set.
+                updateAllVpnsCapabilities();
+                notifyIfacesChangedForNetworkStats();
+            });
         }
         return success;
     }
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index b3084f5..775e4c8 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -22,8 +22,11 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code
@@ -40,13 +43,14 @@
  * time in that process. Once started the UEvent thread will not stop (although it can stop
  * notifying UEventObserver's via stopObserving()).
  *
- * <p>
- *
  * @hide
  */
 public abstract class ExtconUEventObserver extends UEventObserver {
     private static final String TAG = "ExtconUEventObserver";
     private static final boolean LOG = false;
+    private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED =
+            "This probably means the selinux policies need to be changed.";
+
     private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>();
 
     @Override
@@ -64,21 +68,56 @@
      * Subclasses of ExtconUEventObserver should override this method to handle UEvents.
      *
      * @param extconInfo that matches the {@code DEVPATH} of {@code event}
-     * @param event the event
+     * @param event      the event
      */
     protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event);
 
     /** Starts observing {@link ExtconInfo#getDevicePath()}. */
     public void startObserving(ExtconInfo extconInfo) {
-        mExtconInfos.put(extconInfo.getDevicePath(), extconInfo);
-        if (LOG) Slog.v(TAG, "Observing  " + extconInfo.getDevicePath());
-        startObserving("DEVPATH=" + extconInfo.getDevicePath());
+        String devicePath = extconInfo.getDevicePath();
+        if (devicePath == null) {
+            Slog.wtf(TAG, "Unable to start observing  " + extconInfo.getName()
+                    + " because the device path is null. " + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
+        } else {
+            mExtconInfos.put(devicePath, extconInfo);
+            if (LOG) Slog.v(TAG, "Observing  " + devicePath);
+            startObserving("DEVPATH=" + devicePath);
+        }
     }
 
     /** An External Connection to watch. */
     public static final class ExtconInfo {
         private static final String TAG = "ExtconInfo";
 
+        /** Returns a new list of all external connections whose name matches {@code regex}. */
+        public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
+            if (!extconExists()) {
+                return new ArrayList<>(0);  // Always return a new list.
+            }
+            Pattern p = regex == null ? null : Pattern.compile(regex);
+            File file = new File("/sys/class/extcon");
+            File[] files = file.listFiles();
+            if (files == null) {
+                Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory()
+                        + " but listFiles returns null. "
+                        + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
+                return new ArrayList<>(0);  // Always return a new list.
+            } else {
+                ArrayList list = new ArrayList(files.length);
+                for (File f : files) {
+                    String name = f.getName();
+                    if (p == null || p.matcher(name).matches()) {
+                        ExtconInfo uei = new ExtconInfo(name);
+                        list.add(uei);
+                        if (LOG) Slog.d(TAG, name + " matches " + regex);
+                    } else {
+                        if (LOG) Slog.d(TAG, name + " does not match " + regex);
+                    }
+                }
+                return list;
+            }
+        }
+
         private final String mName;
 
         public ExtconInfo(String name) {
@@ -123,6 +162,15 @@
     /** Does the {@link /sys/class/extcon} directory exist */
     public static boolean extconExists() {
         File extconDir = new File("/sys/class/extcon");
-        return extconDir.exists() && extconDir.isDirectory();
+        boolean retVal = extconDir.exists() && extconDir.isDirectory();
+        // TODO(b/124364409): return the correct value after selinux policy is updated.
+        if (retVal) {
+            Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir "
+                    + extconDir.isDirectory()
+                    + " but reporting it does not exist until selinux policies are updated."
+                    + " see b/124364409"
+            );
+        }
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index fe30057..5d0e308 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,9 +17,12 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for regular
  * foreground service operations, which shouldn't be blocked by anything running in
@@ -34,6 +37,7 @@
 
     private static FgThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private FgThread() {
         super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -48,6 +52,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -64,4 +69,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (FgThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index bfe825a..21fd29c 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton I/O thread for the system.  This is a thread for non-background
  * service operations that can potential block briefly on network IO operations
@@ -27,6 +30,7 @@
 public final class IoThread extends ServiceThread {
     private static IoThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private IoThread() {
         super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -38,6 +42,7 @@
             sInstance.start();
             sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -54,4 +59,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (IoThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 4834ce0..5989a46 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -67,6 +67,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -109,6 +110,7 @@
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
 import com.android.server.location.MockProvider;
 import com.android.server.location.PassiveProvider;
+import com.android.server.location.RemoteListenerHelper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -121,6 +123,8 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -225,11 +229,14 @@
     private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>();
 
     @GuardedBy("mLock")
-    private final ArrayMap<IBinder, CallerIdentity> mGnssMeasurementsListeners = new ArrayMap<>();
-
+    private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
+            mGnssMeasurementsListeners = new ArrayMap<>();
     @GuardedBy("mLock")
-    private final ArrayMap<IBinder, CallerIdentity>
+    private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
             mGnssNavigationMessageListeners = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
+            mGnssStatusListeners = new ArrayMap<>();
 
     // current active user on the device - other users are denied location data
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -243,7 +250,7 @@
     @GuardedBy("mLock")
     private IBatchedLocationCallback mGnssBatchingCallback;
     @GuardedBy("mLock")
-    private LinkedCallback mGnssBatchingDeathCallback;
+    private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
     @GuardedBy("mLock")
     private boolean mGnssBatchingInProgress = false;
 
@@ -485,7 +492,7 @@
                         && record.mIsForegroundUid != foreground) {
                     if (D) {
                         Log.d(TAG, "request from uid " + uid + " is now "
-                                + (foreground ? "foreground" : "background)"));
+                                + foregroundAsString(foreground));
                     }
                     record.updateForeground(foreground);
 
@@ -499,44 +506,48 @@
             applyRequirementsLocked(provider);
         }
 
-        for (Entry<IBinder, CallerIdentity> entry : mGnssMeasurementsListeners.entrySet()) {
-            CallerIdentity callerIdentity = entry.getValue();
-            if (callerIdentity.mUid == uid) {
-                if (D) {
-                    Log.d(TAG, "gnss measurements listener from uid " + uid
-                            + " is now " + (foreground ? "foreground" : "background)"));
-                }
-                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                    mGnssMeasurementsProvider.addListener(
-                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
-                            callerIdentity);
-                } else {
-                    mGnssMeasurementsProvider.removeListener(
-                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
-                }
-            }
-        }
+        updateGnssDataProviderOnUidImportanceChangedLocked(mGnssMeasurementsListeners,
+                mGnssMeasurementsProvider, IGnssMeasurementsListener.Stub::asInterface,
+                uid, foreground);
 
-        for (Entry<IBinder, CallerIdentity> entry : mGnssNavigationMessageListeners.entrySet()) {
-            CallerIdentity callerIdentity = entry.getValue();
-            if (callerIdentity.mUid == uid) {
-                if (D) {
-                    Log.d(TAG, "gnss navigation message listener from uid "
-                            + uid + " is now "
-                            + (foreground ? "foreground" : "background)"));
-                }
-                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                    mGnssNavigationMessageProvider.addListener(
-                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
-                            callerIdentity);
-                } else {
-                    mGnssNavigationMessageProvider.removeListener(
-                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
-                }
+        updateGnssDataProviderOnUidImportanceChangedLocked(mGnssNavigationMessageListeners,
+                mGnssNavigationMessageProvider, IGnssNavigationMessageListener.Stub::asInterface,
+                uid, foreground);
+
+        updateGnssDataProviderOnUidImportanceChangedLocked(mGnssStatusListeners,
+                mGnssStatusProvider, IGnssStatusListener.Stub::asInterface, uid, foreground);
+    }
+
+    @GuardedBy("mLock")
+    private <TListener extends IInterface> void updateGnssDataProviderOnUidImportanceChangedLocked(
+            ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
+            RemoteListenerHelper<TListener> gnssDataProvider,
+            Function<IBinder, TListener> mapBinderToListener, int uid, boolean foreground) {
+        for (Entry<IBinder, ? extends LinkedListenerBase> entry : gnssDataListeners.entrySet()) {
+            LinkedListenerBase linkedListener = entry.getValue();
+            CallerIdentity callerIdentity = linkedListener.mCallerIdentity;
+            if (callerIdentity.mUid != uid) {
+                continue;
+            }
+
+            if (D) {
+                Log.d(TAG, linkedListener.mListenerName + " from uid "
+                        + uid + " is now " + foregroundAsString(foreground));
+            }
+
+            TListener listener = mapBinderToListener.apply(entry.getKey());
+            if (foreground || isThrottlingExemptLocked(callerIdentity)) {
+                gnssDataProvider.addListener(listener, callerIdentity);
+            } else {
+                gnssDataProvider.removeListener(listener);
             }
         }
     }
 
+    private static String foregroundAsString(boolean foreground) {
+        return foreground ? "foreground" : "background";
+    }
+
     private static boolean isImportanceForeground(int importance) {
         return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
     }
@@ -1218,9 +1229,8 @@
      * A wrapper class holding either an ILocationListener or a PendingIntent to receive
      * location updates.
      */
-    private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+    private final class Receiver extends LinkedListenerBase implements PendingIntent.OnFinished {
         private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
-        final CallerIdentity mCallerIdentity;
         private final int mAllowedResolutionLevel;  // resolution level allowed to receiver
 
         private final ILocationListener mListener;
@@ -1240,6 +1250,7 @@
 
         private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
                 String packageName, WorkSource workSource, boolean hideFromAppOps) {
+            super(new CallerIdentity(uid, pid, packageName), "LocationListener");
             mListener = listener;
             mPendingIntent = intent;
             if (listener != null) {
@@ -1248,7 +1259,6 @@
                 mKey = intent;
             }
             mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
-            mCallerIdentity = new CallerIdentity(uid, pid, packageName);
             if (workSource != null && workSource.isEmpty()) {
                 workSource = null;
             }
@@ -1486,7 +1496,7 @@
 
         @Override
         public void binderDied() {
-            if (D) Log.d(TAG, "Location listener died");
+            if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
 
             synchronized (mLock) {
                 removeUpdatesLocked(this);
@@ -1617,53 +1627,29 @@
             return false;
         }
 
+        CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
+                Binder.getCallingPid(), packageName);
         synchronized (mLock) {
             mGnssBatchingCallback = callback;
-            mGnssBatchingDeathCallback = new LinkedCallback(callback);
-            try {
-                callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */);
-            } catch (RemoteException e) {
-                // if the remote process registering the listener is already dead, just swallow the
-                // exception and return
-                Log.e(TAG, "Remote listener already died.", e);
+            mGnssBatchingDeathCallback =  new LinkedListener<>(callback,
+                    "BatchedLocationCallback", callerIdentity,
+                    (IBatchedLocationCallback listener) -> {
+                        stopGnssBatch();
+                        removeGnssBatchingCallback();
+                    });
+            if (!linkToListenerDeathNotificationLocked(callback.asBinder(),
+                    mGnssBatchingDeathCallback)) {
                 return false;
             }
-
             return true;
         }
     }
 
-    private class LinkedCallback implements IBinder.DeathRecipient {
-        private final IBatchedLocationCallback mCallback;
-
-        private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
-            mCallback = callback;
-        }
-
-        @NonNull
-        public IBatchedLocationCallback getUnderlyingListener() {
-            return mCallback;
-        }
-
-        @Override
-        public void binderDied() {
-            Log.d(TAG, "Remote Batching Callback died: " + mCallback);
-            stopGnssBatch();
-            removeGnssBatchingCallback();
-        }
-    }
-
     @Override
     public void removeGnssBatchingCallback() {
         synchronized (mLock) {
-            try {
-                mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback,
-                        0 /* flags */);
-            } catch (NoSuchElementException e) {
-                // if the death callback isn't connected (it should be...), log error, swallow the
-                // exception and return
-                Log.e(TAG, "Couldn't unlink death callback.", e);
-            }
+            unlinkFromListenerDeathNotificationLocked(mGnssBatchingCallback.asBinder(),
+                    mGnssBatchingDeathCallback);
             mGnssBatchingCallback = null;
             mGnssBatchingDeathCallback = null;
         }
@@ -2053,7 +2039,7 @@
                 }
                 if (!provider.isUseableLocked()) {
                     if (isSettingsExemptLocked(record)) {
-                        providerRequest.forceLocation = true;
+                        providerRequest.locationSettingsIgnored = true;
                         providerRequest.lowPowerMode = false;
                     } else {
                         continue;
@@ -2063,8 +2049,9 @@
                 LocationRequest locationRequest = record.mRealRequest;
                 long interval = locationRequest.getInterval();
 
+
                 // if we're forcing location, don't apply any throttling
-                if (!providerRequest.forceLocation && !isThrottlingExemptLocked(
+                if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked(
                         record.mReceiver.mCallerIdentity)) {
                     if (!record.mIsForegroundUid) {
                         interval = Math.max(interval, backgroundThrottleInterval);
@@ -2149,6 +2136,13 @@
         }
     }
 
+    @Override
+    public String[] getIgnoreSettingsWhitelist() {
+        synchronized (mLock) {
+            return mIgnoreSettingsPackageWhitelist.toArray(new String[0]);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
         if (callerIdentity.mUid == Process.SYSTEM_UID) {
@@ -2264,10 +2258,8 @@
         if (receiver == null) {
             receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
                     hideFromAppOps);
-            try {
-                receiver.getListener().asBinder().linkToDeath(receiver, 0);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "linkToDeath failed:", e);
+            if (!linkToListenerDeathNotificationLocked(receiver.getListener().asBinder(),
+                    receiver)) {
                 return null;
             }
             mReceivers.put(binder, receiver);
@@ -2482,7 +2474,8 @@
         if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
 
         if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
-            receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
+            unlinkFromListenerDeathNotificationLocked(receiver.getListener().asBinder(),
+                    receiver);
             receiver.clearPendingBroadcastsLocked();
         }
 
@@ -2694,43 +2687,135 @@
     }
 
     @Override
-    public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
-            return false;
-        }
-
-        return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(),
-                Binder.getCallingPid(), packageName));
+    public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
+        return addGnssDataListener(listener, packageName, "GnssStatusListener",
+                mGnssStatusProvider, mGnssStatusListeners,
+                this::unregisterGnssStatusCallback);
     }
 
     @Override
-    public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
-        mGnssStatusProvider.removeListener(callback);
+    public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
+        removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
     }
 
     @Override
     public boolean addGnssMeasurementsListener(
             IGnssMeasurementsListener listener, String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+        return addGnssDataListener(listener, packageName, "GnssMeasurementsListener",
+                mGnssMeasurementsProvider, mGnssMeasurementsListeners,
+                this::removeGnssMeasurementsListener);
+    }
+
+    @Override
+    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+        removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
+    }
+
+    private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
+        protected final CallerIdentity mCallerIdentity;
+        protected final String mListenerName;
+
+        private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
+                @NonNull String listenerName) {
+            mCallerIdentity = callerIdentity;
+            mListenerName = listenerName;
+        }
+    }
+
+    private static class LinkedListener<TListener> extends LinkedListenerBase {
+        private final TListener mListener;
+        private final Consumer<TListener> mBinderDeathCallback;
+
+        private LinkedListener(@NonNull TListener listener, String listenerName,
+                @NonNull CallerIdentity callerIdentity,
+                @NonNull Consumer<TListener> binderDeathCallback) {
+            super(callerIdentity, listenerName);
+            mListener = listener;
+            mBinderDeathCallback = binderDeathCallback;
+        }
+
+        @Override
+        public void binderDied() {
+            if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
+            mBinderDeathCallback.accept(mListener);
+        }
+    }
+
+    private <TListener extends IInterface> boolean addGnssDataListener(
+            TListener listener, String packageName, String listenerName,
+            RemoteListenerHelper<TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
+            Consumer<TListener> binderDeathCallback) {
+        if (!hasGnssPermissions(packageName) || gnssDataProvider == null) {
             return false;
         }
 
+        CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
+                Binder.getCallingPid(), packageName);
+        LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+                listenerName, callerIdentity, binderDeathCallback);
+        IBinder binder = listener.asBinder();
         synchronized (mLock) {
-            CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
-                    Binder.getCallingPid(), packageName);
-            mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
+            if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
+                return false;
+            }
+
+            gnssDataListeners.put(binder, linkedListener);
             long identity = Binder.clearCallingIdentity();
             try {
                 if (isThrottlingExemptLocked(callerIdentity)
                         || isImportanceForeground(
                         mActivityManager.getPackageImportance(packageName))) {
-                    return mGnssMeasurementsProvider.addListener(listener, callerIdentity);
+                    gnssDataProvider.addListener(listener, callerIdentity);
                 }
+                return true;
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
+        }
+    }
 
+    private <TListener extends IInterface> void removeGnssDataListener(
+            TListener listener, RemoteListenerHelper<TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
+        if (gnssDataProvider == null) {
+            return;
+        }
+
+        IBinder binder = listener.asBinder();
+        synchronized (mLock) {
+            LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder);
+            if (linkedListener == null) {
+                return;
+            }
+            unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
+            gnssDataProvider.removeListener(listener);
+        }
+    }
+
+    private boolean linkToListenerDeathNotificationLocked(IBinder binder,
+            LinkedListenerBase linkedListener) {
+        try {
+            binder.linkToDeath(linkedListener, 0 /* flags */);
             return true;
+        } catch (RemoteException e) {
+            // if the remote process registering the listener is already dead, just swallow the
+            // exception and return
+            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e);
+            return false;
+        }
+    }
+
+    private boolean unlinkFromListenerDeathNotificationLocked(IBinder binder,
+            LinkedListenerBase linkedListener) {
+        try {
+            binder.unlinkToDeath(linkedListener, 0 /* flags */);
+            return true;
+        } catch (NoSuchElementException e) {
+            // if the death callback isn't connected (it should be...), log error,
+            // swallow the exception and return
+            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e);
+            return false;
         }
     }
 
@@ -2759,53 +2844,17 @@
     }
 
     @Override
-    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
-        if (mGnssMeasurementsProvider == null) {
-            return;
-        }
-
-        synchronized (mLock) {
-            mGnssMeasurementsListeners.remove(listener.asBinder());
-            mGnssMeasurementsProvider.removeListener(listener);
-        }
-    }
-
-    @Override
     public boolean addGnssNavigationMessageListener(
-            IGnssNavigationMessageListener listener,
-            String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
-            return false;
-        }
-
-        synchronized (mLock) {
-            CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
-                    Binder.getCallingPid(), packageName);
-
-            mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (isThrottlingExemptLocked(callerIdentity)
-                        || isImportanceForeground(
-                        mActivityManager.getPackageImportance(packageName))) {
-                    return mGnssNavigationMessageProvider.addListener(listener, callerIdentity);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-
-            return true;
-        }
+            IGnssNavigationMessageListener listener, String packageName) {
+        return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener",
+                mGnssNavigationMessageProvider, mGnssNavigationMessageListeners,
+                this::removeGnssNavigationMessageListener);
     }
 
     @Override
     public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
-        if (mGnssNavigationMessageProvider != null) {
-            synchronized (mLock) {
-                mGnssNavigationMessageListeners.remove(listener.asBinder());
-                mGnssNavigationMessageProvider.removeListener(listener);
-            }
-        }
+        removeGnssDataListener(listener, mGnssNavigationMessageProvider,
+                mGnssNavigationMessageListeners);
     }
 
     @Override
@@ -3368,18 +3417,14 @@
                     pw.println("      " + record);
                 }
             }
+
             pw.println("  Active GnssMeasurement Listeners:");
-            for (CallerIdentity callerIdentity : mGnssMeasurementsListeners.values()) {
-                pw.println("    " + callerIdentity.mPid + " " + callerIdentity.mUid + " "
-                        + callerIdentity.mPackageName + ": "
-                        + isThrottlingExemptLocked(callerIdentity));
-            }
+            dumpGnssDataListenersLocked(pw, mGnssMeasurementsListeners);
             pw.println("  Active GnssNavigationMessage Listeners:");
-            for (CallerIdentity callerIdentity : mGnssNavigationMessageListeners.values()) {
-                pw.println("    " + callerIdentity.mPid + " " + callerIdentity.mUid + " "
-                        + callerIdentity.mPackageName + ": "
-                        + isThrottlingExemptLocked(callerIdentity));
-            }
+            dumpGnssDataListenersLocked(pw, mGnssNavigationMessageListeners);
+            pw.println("  Active GnssStatus Listeners:");
+            dumpGnssDataListenersLocked(pw, mGnssStatusListeners);
+
             pw.println("  Historical Records by Provider:");
             for (Map.Entry<PackageProviderKey, PackageStatistics> entry
                     : mRequestStatistics.statistics.entrySet()) {
@@ -3432,4 +3477,15 @@
             }
         }
     }
+
+    @GuardedBy("mLock")
+    private void dumpGnssDataListenersLocked(PrintWriter pw,
+            ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners) {
+        for (LinkedListenerBase listener : gnssDataListeners.values()) {
+            CallerIdentity callerIdentity = listener.mCallerIdentity;
+            pw.println("    " + callerIdentity.mPid + " " + callerIdentity.mUid + " "
+                    + callerIdentity.mPackageName + ": "
+                    + isThrottlingExemptLocked(callerIdentity));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f505b76..dc394d0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,18 +20,18 @@
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_BLACKLIST;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_NONE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.INetd.FIREWALL_WHITELIST;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_ALL;
@@ -1946,7 +1946,7 @@
 
         int numUids = 0;
         if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
-        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+        if (getFirewallType(chain) == FIREWALL_WHITELIST) {
             // Close all sockets on all non-system UIDs...
             ranges = new UidRange[] {
                 // TODO: is there a better way of finding all existing users? If so, we could
@@ -1958,7 +1958,7 @@
                 final SparseIntArray rules = getUidFirewallRulesLR(chain);
                 exemptUids = new int[rules.size()];
                 for (int i = 0; i < exemptUids.length; i++) {
-                    if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+                    if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) {
                         exemptUids[numUids] = rules.keyAt(i);
                         numUids++;
                     }
@@ -1980,7 +1980,7 @@
                 final SparseIntArray rules = getUidFirewallRulesLR(chain);
                 ranges = new UidRange[rules.size()];
                 for (int i = 0; i < ranges.length; i++) {
-                    if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+                    if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
                         int uid = rules.keyAt(i);
                         ranges[numUids] = new UidRange(uid, uid);
                         numUids++;
@@ -2052,13 +2052,13 @@
     private int getFirewallType(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_STANDBY:
-                return FIREWALL_TYPE_BLACKLIST;
+                return FIREWALL_BLACKLIST;
             case FIREWALL_CHAIN_DOZABLE:
-                return FIREWALL_TYPE_WHITELIST;
+                return FIREWALL_WHITELIST;
             case FIREWALL_CHAIN_POWERSAVE:
-                return FIREWALL_TYPE_WHITELIST;
+                return FIREWALL_WHITELIST;
             default:
-                return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
+                return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST;
         }
     }
 
@@ -2160,14 +2160,14 @@
 
     private @NonNull String getFirewallRuleName(int chain, int rule) {
         String ruleName;
-        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
-            if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+        if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+            if (rule == FIREWALL_RULE_ALLOW) {
                 ruleName = "allow";
             } else {
                 ruleName = "deny";
             }
         } else { // Blacklist mode
-            if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+            if (rule == FIREWALL_RULE_DENY) {
                 ruleName = "deny";
             } else {
                 ruleName = "allow";
@@ -2194,7 +2194,7 @@
 
     private int getFirewallRuleType(int chain, int rule) {
         if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
-            return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST
+            return getFirewallType(chain) == FIREWALL_WHITELIST
                     ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
         }
         return rule;
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 62da3f8..6e5d316 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -37,6 +37,7 @@
 import android.util.StatsLog;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.FlagNamespaceUtils;
 
 import java.io.File;
 
@@ -194,6 +195,8 @@
                 RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
                 break;
         }
+        FlagNamespaceUtils.addToKnownResetNamespaces(
+                FlagNamespaceUtils.NAMESPACE_NO_PACKAGE);
     }
 
     private static void resetAllSettings(Context context, int mode) throws Exception {
@@ -203,14 +206,19 @@
         final ContentResolver resolver = context.getContentResolver();
         try {
             Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
-        } catch (Throwable t) {
-            res = new RuntimeException("Failed to reset global settings", t);
+        } catch (Exception e) {
+            res = new RuntimeException("Failed to reset global settings", e);
+        }
+        try {
+            FlagNamespaceUtils.resetDeviceConfig(mode);
+        } catch (Exception e) {
+            res = new RuntimeException("Failed to reset config settings", e);
         }
         for (int userId : getAllUserIds()) {
             try {
                 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
-            } catch (Throwable t) {
-                res = new RuntimeException("Failed to reset secure settings for " + userId, t);
+            } catch (Exception e) {
+                res = new RuntimeException("Failed to reset secure settings for " + userId, e);
             }
         }
         if (res != null) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e7d7434..cd9d84c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -860,7 +860,7 @@
         } else if (remote == 1) {
             res = true;
         } else {
-            res = false;
+            res = true;
         }
 
         Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag "
@@ -1533,7 +1533,7 @@
 
         // Snapshot feature flag used for this boot
         SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
-                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
+                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
 
         mContext = context;
         mResolver = mContext.getContentResolver();
@@ -3688,7 +3688,16 @@
             } else if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) {
                 return Zygote.MOUNT_EXTERNAL_NONE;
             } else {
-                return Zygote.MOUNT_EXTERNAL_WRITE;
+                // STOPSHIP: remove this temporary workaround once developers
+                // fix bugs where they're opening _data paths in native code
+                switch (packageName) {
+                    case "com.facebook.katana": // b/123996076
+                    case "jp.naver.line.android": // b/124767356
+                    case "com.mxtech.videoplayer.ad": // b/124531483
+                        return Zygote.MOUNT_EXTERNAL_LEGACY;
+                    default:
+                        return Zygote.MOUNT_EXTERNAL_WRITE;
+                }
             }
         } catch (RemoteException e) {
             // Should not happen
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2f1510e..fd946cd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -236,7 +237,7 @@
 
     private PhoneCapability mPhoneCapability = null;
 
-    private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     @TelephonyManager.RadioPowerState
     private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
@@ -246,14 +247,18 @@
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
-    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+    // Nothing here yet, but putting it here in case we want to add more in the future.
+    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+
+    static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CELL_LOCATION
                     | PhoneStateListener.LISTEN_CELL_INFO;
 
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                         | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+                        | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -637,8 +642,14 @@
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
                             if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
-                            r.callback.onServiceStateChanged(
-                                    new ServiceState(mServiceState[phoneId]));
+                            ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+                            } else {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -673,7 +684,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellLocation = "
                                     + mCellLocation[phoneId]);
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellLocationChanged(
                                         new Bundle(mCellLocation[phoneId]));
                             }
@@ -722,7 +733,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                     + mCellInfo.get(phoneId));
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                             }
                         } catch (RemoteException ex) {
@@ -809,9 +820,9 @@
                             remove(r.binder);
                         }
                     }
-                    if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) {
+                    if ((events & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE) != 0) {
                         try {
-                            r.callback.onPreferredDataSubIdChanged(mPreferredDataSubId);
+                            r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -1009,13 +1020,22 @@
                     }
                     if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
                             idMatch(r.subId, subId, phoneId)) {
+
                         try {
+                            ServiceState stateToSend;
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = new ServiceState(state);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = state.sanitizeLocationInfo(false);
+                            } else {
+                                stateToSend = state.sanitizeLocationInfo(true);
+                            }
                             if (DBG) {
                                 log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " state=" + state);
                             }
-                            r.callback.onServiceStateChanged(new ServiceState(state));
+                            r.callback.onServiceStateChanged(stateToSend);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1198,7 +1218,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1500,7 +1520,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1738,23 +1758,23 @@
         }
     }
 
-    public void notifyPreferredDataSubIdChanged(int preferredSubId) {
-        if (!checkNotifyPermission("notifyPreferredDataSubIdChanged()")) {
+    public void notifyActiveDataSubIdChanged(int activeDataSubId) {
+        if (!checkNotifyPermission("notifyActiveDataSubIdChanged()")) {
             return;
         }
 
         if (VDBG) {
-            log("notifyPreferredDataSubIdChanged: preferredSubId=" + preferredSubId);
+            log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
         }
 
         synchronized (mRecords) {
-            mPreferredDataSubId = preferredSubId;
+            mActiveDataSubId = activeDataSubId;
 
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE)) {
+                        PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE)) {
                     try {
-                        r.callback.onPreferredDataSubIdChanged(preferredSubId);
+                        r.callback.onActiveDataSubIdChanged(activeDataSubId);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -1890,7 +1910,7 @@
             pw.println("mBackgroundCallState=" + mBackgroundCallState);
             pw.println("mSrvccState=" + mSrvccState);
             pw.println("mPhoneCapability=" + mPhoneCapability);
-            pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
+            pw.println("mActiveDataSubId=" + mActiveDataSubId);
             pw.println("mRadioPowerState=" + mRadioPowerState);
             pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
             pw.println("mCallQuality=" + mCallQuality);
@@ -2108,12 +2128,35 @@
 
     private boolean checkListenerPermission(
             int events, int subId, String callingPackage, String message) {
+        LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                .setCallingPackage(callingPackage)
+                .setMethod(message + " events: " + events)
+                .setCallingPid(Binder.getCallingPid())
+                .setCallingUid(Binder.getCallingUid());
+
+        boolean shouldCheckLocationPermissions = false;
         if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
-            if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return false;
+            locationQueryBuilder.setMinSdkVersionForCoarse(0);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+            // Everything that requires fine location started in Q. So far...
+            locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if (shouldCheckLocationPermissions) {
+            LocationAccessPolicy.LocationPermissionResult result =
+                    LocationAccessPolicy.checkLocationPermission(
+                            mContext, locationQueryBuilder.build());
+            switch (result) {
+                case DENIED_HARD:
+                    throw new SecurityException("Unable to listen for events " + events + " due to "
+                            + "insufficient location permissions.");
+                case DENIED_SOFT:
+                    return false;
             }
         }
 
@@ -2139,14 +2182,6 @@
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
 
-        if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) {
-            // It can have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE.
-            TelephonyPermissions.checkReadPhoneState(mContext,
-                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
-                    Binder.getCallingUid(), callingPackage, "listen to "
-                            + "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
-        }
-
         if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
@@ -2228,15 +2263,38 @@
         }
     }
 
-    private boolean checkLocationAccess(Record r) {
-        long token = Binder.clearCallingIdentity();
-        try {
-            return LocationAccessPolicy.canAccessCellLocation(mContext,
-                    r.callingPackage, r.callerUid, r.callerPid,
-                    /*throwOnDeniedPermission*/ false);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+    private boolean checkFineLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForFine(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
+    }
+
+    private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForCoarse(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
     }
 
     private void checkPossibleMissNotify(Record r, int phoneId) {
@@ -2286,7 +2344,7 @@
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
                             + mCellInfo.get(phoneId));
                 }
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                 }
             } catch (RemoteException ex) {
@@ -2336,7 +2394,7 @@
             try {
                 if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
                         + mCellLocation[phoneId]);
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
                 }
             } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b57fcd..710a0ba3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -30,11 +30,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -354,8 +356,12 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
-                        Settings.Secure.putIntForUser(getContext().getContentResolver(),
-                                Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        // Only persist setting if not transient night mode or not in car mode
+                        if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) {
+                            Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                                    Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        }
+
                         mNightMode = mode;
                         updateLocked(0, 0);
                     }
@@ -438,9 +444,39 @@
         }
     }
 
+    // Night mode settings in car mode are only persisted below Q.
+    // When targeting Q, changes are not saved and night mode will be re-read
+    // from settings when exiting car mode.
+    private boolean shouldTransientNightWhenInCarMode() {
+        int uid = Binder.getCallingUid();
+        PackageManager packageManager = getContext().getPackageManager();
+        String[] packagesForUid = packageManager.getPackagesForUid(uid);
+        if (packagesForUid == null || packagesForUid.length == 0) {
+            return false;
+        }
+
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+                    packagesForUid[0], 0, uid);
+
+            return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+
+        return false;
+    }
+
     void setCarModeLocked(boolean enabled, int flags) {
         if (mCarModeEnabled != enabled) {
             mCarModeEnabled = enabled;
+
+            // When transient night mode and exiting car mode, restore night mode from settings
+            if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) {
+                Context context = getContext();
+                updateNightModeFromSettings(context,
+                        context.getResources(),
+                        UserHandle.getCallingUserId());
+            }
         }
         mCarModeEnableFlags = flags;
     }
@@ -498,7 +534,9 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave) {
+        // Override night mode in power save mode if not transient night mode or not in car mode
+        boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode();
+        if (mPowerSave && shouldOverrideNight) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 3939bee..9bbc315 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -23,6 +23,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.UEventObserver;
+import android.util.Pair;
 import android.util.Slog;
 import android.media.AudioManager;
 import android.util.Log;
@@ -31,6 +32,7 @@
 import com.android.internal.R;
 import com.android.server.input.InputManagerService;
 import com.android.server.input.InputManagerService.WiredAccessoryCallbacks;
+
 import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT;
 import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT;
 import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT;
@@ -41,6 +43,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -52,7 +55,7 @@
  */
 final class WiredAccessoryManager implements WiredAccessoryCallbacks {
     private static final String TAG = WiredAccessoryManager.class.getSimpleName();
-    private static final boolean LOG = true;
+    private static final boolean LOG = false;
 
     private static final int BIT_HEADSET = (1 << 0);
     private static final int BIT_HEADSET_NO_MIC = (1 << 1);
@@ -60,9 +63,9 @@
     private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
     private static final int BIT_HDMI_AUDIO = (1 << 4);
     private static final int BIT_LINEOUT = (1 << 5);
-    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
-                                                   BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
-                                                   BIT_HDMI_AUDIO|BIT_LINEOUT);
+    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC |
+            BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL |
+            BIT_HDMI_AUDIO | BIT_LINEOUT);
 
     private static final String NAME_H2W = "h2w";
     private static final String NAME_USB_AUDIO = "usb_audio";
@@ -82,30 +85,34 @@
     private int mSwitchValues;
 
     private final WiredAccessoryObserver mObserver;
+    private final WiredAccessoryExtconObserver mExtconObserver;
     private final InputManagerService mInputManager;
 
     private final boolean mUseDevInputEventForAudioJack;
 
     public WiredAccessoryManager(Context context, InputManagerService inputManager) {
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager");
         mWakeLock.setReferenceCounted(false);
-        mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mInputManager = inputManager;
 
         mUseDevInputEventForAudioJack =
                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
 
+        mExtconObserver = new WiredAccessoryExtconObserver();
         mObserver = new WiredAccessoryObserver();
     }
 
     private void onSystemReady() {
         if (mUseDevInputEventForAudioJack) {
             int switchValues = 0;
-            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) {
+            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT)
+                    == 1) {
                 switchValues |= SW_HEADPHONE_INSERT_BIT;
             }
-            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) {
+            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT)
+                    == 1) {
                 switchValues |= SW_MICROPHONE_INSERT_BIT;
             }
             if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) {
@@ -115,20 +122,31 @@
                     SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT);
         }
 
-        mObserver.init();
+
+        if (ExtconUEventObserver.extconExists()) {
+            if (mUseDevInputEventForAudioJack) {
+                Log.w(TAG, "Both input event and extcon are used for audio jack,"
+                        + " please just choose one.");
+            }
+            mExtconObserver.init();
+        } else {
+            mObserver.init();
+        }
     }
 
     @Override
     public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
-        if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
-                + " bits=" + switchCodeToString(switchValues, switchMask)
-                + " mask=" + Integer.toHexString(switchMask));
+        if (LOG) {
+            Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
+                    + " bits=" + switchCodeToString(switchValues, switchMask)
+                    + " mask=" + Integer.toHexString(switchMask));
+        }
 
         synchronized (mLock) {
             int headset;
             mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
             switch (mSwitchValues &
-                (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
+                    (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
                 case 0:
                     headset = 0;
                     break;
@@ -155,7 +173,7 @@
             }
 
             updateLocked(NAME_H2W,
-                (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
+                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
         }
     }
 
@@ -175,7 +193,7 @@
      * results in support for the last one plugged in. Similarly, unplugging either is seen as
      * unplugging all.
      *
-     * @param newName One of the NAME_xxx variables defined above.
+     * @param newName  One of the NAME_xxx variables defined above.
      * @param newState 0 or one of the BIT_xxx variables defined above.
      */
     private void updateLocked(String newName, int newState) {
@@ -186,10 +204,12 @@
         int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT);
         boolean h2wStateChange = true;
         boolean usbStateChange = true;
-        if (LOG) Slog.v(TAG, "newName=" + newName
-                + " newState=" + newState
-                + " headsetState=" + headsetState
-                + " prev headsetState=" + mHeadsetState);
+        if (LOG) {
+            Slog.v(TAG, "newName=" + newName
+                    + " newState=" + newState
+                    + " headsetState=" + headsetState
+                    + " prev headsetState=" + mHeadsetState);
+        }
 
         if (mHeadsetState == headsetState) {
             Log.e(TAG, "No state change.");
@@ -229,7 +249,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_NEW_DEVICE_STATE:
-                    setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
+                    setDevicesState(msg.arg1, msg.arg2, (String) msg.obj);
                     mWakeLock.release();
                     break;
                 case MSG_SYSTEM_READY:
@@ -269,9 +289,9 @@
             if (headset == BIT_HEADSET) {
                 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
                 inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
-            } else if (headset == BIT_HEADSET_NO_MIC){
+            } else if (headset == BIT_HEADSET_NO_MIC) {
                 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
-            } else if (headset == BIT_LINEOUT){
+            } else if (headset == BIT_LINEOUT) {
                 outDevice = AudioManager.DEVICE_OUT_LINE;
             } else if (headset == BIT_USB_HEADSET_ANLG) {
                 outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
@@ -280,7 +300,7 @@
             } else if (headset == BIT_HDMI_AUDIO) {
                 outDevice = AudioManager.DEVICE_OUT_HDMI;
             } else {
-                Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
+                Slog.e(TAG, "setDeviceState() invalid headset type: " + headset);
                 return;
             }
 
@@ -290,10 +310,10 @@
             }
 
             if (outDevice != 0) {
-              mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
+                mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
             }
             if (inDevice != 0) {
-              mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);
+                mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);
             }
         }
     }
@@ -340,7 +360,7 @@
                                 " not found while attempting to determine initial switch state");
                     } catch (Exception e) {
                         Slog.e(TAG, "Error while attempting to determine initial switch state for "
-                                + uei.getDevName() , e);
+                                + uei.getDevName(), e);
                     }
                 }
             }
@@ -350,7 +370,7 @@
             // observe three UEVENTs
             for (int i = 0; i < mUEventInfo.size(); ++i) {
                 UEventInfo uei = mUEventInfo.get(i);
-                startObserving("DEVPATH="+uei.getDevPath());
+                startObserving("DEVPATH=" + uei.getDevPath());
             }
         }
 
@@ -438,7 +458,9 @@
                 mStateNbits = stateNbits;
             }
 
-            public String getDevName() { return mDevName; }
+            public String getDevName() {
+                return mDevName;
+            }
 
             public String getDevPath() {
                 return String.format(Locale.US, "/devices/virtual/switch/%s", mDevName);
@@ -456,11 +478,84 @@
             public int computeNewHeadsetState(int headsetState, int switchState) {
                 int preserveMask = ~(mState1Bits | mState2Bits | mStateNbits);
                 int setBits = ((switchState == 1) ? mState1Bits :
-                              ((switchState == 2) ? mState2Bits :
-                              ((switchState == mStateNbits) ? mStateNbits : 0)));
+                        ((switchState == 2) ? mState2Bits :
+                                ((switchState == mStateNbits) ? mStateNbits : 0)));
 
                 return ((headsetState & preserveMask) | setBits);
             }
         }
     }
+
+    private class WiredAccessoryExtconObserver extends ExtconStateObserver<Pair<Integer, Integer>> {
+        private final List<ExtconInfo> mExtconInfos;
+
+        WiredAccessoryExtconObserver() {
+            mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*");
+
+        }
+
+        private void init() {
+            for (ExtconInfo extconInfo : mExtconInfos) {
+                Pair<Integer, Integer> state = null;
+                try {
+                    state = parseStateFromFile(extconInfo);
+                } catch (FileNotFoundException e) {
+                    Slog.w(TAG, extconInfo.getStatePath()
+                            + " not found while attempting to determine initial state", e);
+                } catch (IOException e) {
+                    Slog.e(
+                            TAG,
+                            "Error reading " + extconInfo.getStatePath()
+                                    + " while attempting to determine initial state",
+                            e);
+                }
+                if (state != null) {
+                    updateState(extconInfo, extconInfo.getName(), state);
+                }
+                if (LOG) Slog.d(TAG, "observing " + extconInfo.getName());
+                startObserving(extconInfo);
+            }
+
+        }
+
+        @Override
+        public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) {
+            if (LOG) Slog.v(TAG, "status  " + status);
+            int []maskAndState = {0,0};
+            // extcon event state changes from kernel4.9
+            // new state will be like STATE=MICROPHONE=1\nHEADPHONE=0
+            updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ;
+            updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ;
+            updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ;
+            updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ;
+            if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]);
+            return Pair.create(maskAndState[0],maskAndState[1]);
+        }
+
+        @Override
+        public void updateState(ExtconInfo extconInfo, String name,
+                Pair<Integer, Integer> maskAndState) {
+            synchronized (mLock) {
+                int mask = maskAndState.first;
+                int state = maskAndState.second;
+                updateLocked(name, mHeadsetState | (mask & state) & ~(mask & ~state));
+                return;
+            }
+        }
+    }
+
+    /**
+     * Updates the mask bit at {@code position} to 1 and the state bit at {@code position} to true
+     * if {@code name=1}  or false if {}@code name=0} is contained in {@code state}.
+     */
+    private static void updateBit(int[] maskAndState, int position, String state, String name) {
+        maskAndState[0] |= position;
+        if (state.contains(name + "=1")) {
+            maskAndState[0] |= position;
+            maskAndState[1] |= position;
+        } else if (state.contains(name + "=0")) {
+            maskAndState[0] |= position;
+            maskAndState[1] &= ~position;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 026430b..aebe2b7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -319,10 +319,6 @@
                 ServiceRecord r = mDelayedStartList.remove(0);
                 if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                         "REM FR DELAY LIST (exec next): " + r);
-                if (r.pendingStarts.size() <= 0) {
-                    Slog.w(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested
-                            + " delayedStop=" + r.delayedStop);
-                }
                 if (DEBUG_DELAYED_SERVICE) {
                     if (mDelayedStartList.size() > 0) {
                         Slog.v(TAG_SERVICE, "Remaining delayed list:");
@@ -332,11 +328,16 @@
                     }
                 }
                 r.delayed = false;
-                try {
-                    startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
-                            false);
-                } catch (TransactionTooLargeException e) {
-                    // Ignore, nobody upstack cares.
+                if (r.pendingStarts.size() <= 0) {
+                    Slog.wtf(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested
+                            + " delayedStop=" + r.delayedStop);
+                } else {
+                    try {
+                        startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
+                                false);
+                    } catch (TransactionTooLargeException e) {
+                        // Ignore, nobody upstack cares.
+                    }
                 }
             }
             if (mStartingBackground.size() > 0) {
@@ -2978,6 +2979,7 @@
         // Clear start entries.
         r.clearDeliveredStartsLocked();
         r.pendingStarts.clear();
+        smap.mDelayedStartList.remove(r);
 
         if (r.app != null) {
             synchronized (r.stats.getBatteryStats()) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 60a45bf..bfe1f6a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2704,8 +2704,8 @@
     public void batterySendBroadcast(Intent intent) {
         synchronized (this) {
             broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                    OP_NONE, null, false, false,
-                    -1, SYSTEM_UID, UserHandle.USER_ALL);
+                    OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
+                    Binder.getCallingPid(), UserHandle.USER_ALL);
         }
     }
 
@@ -3823,12 +3823,13 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
-                                null, null, permission.ACCESS_INSTANT_APPS, null, false, false,
-                                resolvedUserId, false);
+                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
+                                null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false,
+                                false, resolvedUserId, false);
                     } else {
-                        broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
-                                null, null, null, null, false, false, resolvedUserId, false);
+                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
+                                null, 0, null, null, null, null, false, false, resolvedUserId,
+                                false);
                     }
 
                     if (observer != null) {
@@ -4263,7 +4264,8 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null, OP_NONE,
-                null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
+                null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                Binder.getCallingPid(), UserHandle.getUserId(uid));
     }
 
     private void cleanupDisabledPackageComponentsLocked(
@@ -6630,8 +6632,7 @@
 
                 String msg;
                 if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
-                    throw new SecurityException("Content provider lookup "
-                            + cpr.name.flattenToShortString()
+                    throw new SecurityException("Content provider lookup " + name
                             + " failed: association not allowed with package " + msg);
                 }
                 checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
@@ -8710,6 +8711,8 @@
 
             mAtmInternal.showSystemReadyErrorDialogsIfNeeded();
 
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
             long ident = Binder.clearCallingIdentity();
             try {
                 Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -8718,7 +8721,7 @@
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null, null, OP_NONE,
-                        null, false, false, MY_PID, SYSTEM_UID,
+                        null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
                         currentUserId);
                 intent = new Intent(Intent.ACTION_USER_STARTING);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -8732,7 +8735,8 @@
                             }
                         }, 0, null, null,
                         new String[] {INTERACT_ACROSS_USERS}, OP_NONE,
-                        null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+                        null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
+                        UserHandle.USER_ALL);
             } catch (Throwable t) {
                 Slog.wtf(TAG, "Failed sending first user broadcasts", t);
             } finally {
@@ -14370,10 +14374,12 @@
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
-            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
+            boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
+            int realCallingPid, int userId) {
         return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
             resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
-            sticky, callingPid, callingUid, userId, false /* allowBackgroundActivityStarts */);
+            sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
+            false /* allowBackgroundActivityStarts */);
     }
 
     @GuardedBy("this")
@@ -14381,8 +14387,8 @@
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
-            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId,
-            boolean allowBackgroundActivityStarts) {
+            boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
+            int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
         intent = new Intent(intent);
 
         final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -14431,7 +14437,7 @@
                 // PendingIntent), because that who is actually supplied the arguments.
                 if (checkComponentPermission(
                         android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
-                        Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
+                        realCallingPid, realCallingUid, -1, true)
                         != PackageManager.PERMISSION_GRANTED) {
                     String msg = "Permission Denial: " + intent.getAction()
                             + " broadcast from " + callerPackage + " (pid=" + callingPid
@@ -14449,6 +14455,25 @@
                         + " has background restrictions");
                 return ActivityManager.START_CANCELED;
             }
+            if (brOptions.allowsBackgroundActivityStarts()) {
+                // See if the caller is allowed to do this.  Note we are checking against
+                // the actual real caller (not whoever provided the operation as say a
+                // PendingIntent), because that who is actually supplied the arguments.
+                if (checkComponentPermission(
+                        android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    String msg = "Permission Denial: " + intent.getAction()
+                            + " broadcast from " + callerPackage + " (pid=" + callingPid
+                            + ", uid=" + callingUid + ")"
+                            + " requires "
+                            + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                } else {
+                    allowBackgroundActivityStarts = true;
+                }
+            }
         }
 
         // Verify that protected broadcasts are only being sent by system code,
@@ -15119,15 +15144,15 @@
                     callerApp != null ? callerApp.info.packageName : null,
                     intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                     requiredPermissions, appOp, bOptions, serialized, sticky,
-                    callingPid, callingUid, userId);
+                    callingPid, callingUid, callingUid, callingPid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
     }
 
 
-    int broadcastIntentInPackage(String packageName, int uid,
-            Intent intent, String resolvedType, IIntentReceiver resultTo,
+    int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
+            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
             int userId, boolean allowBackgroundActivityStarts) {
@@ -15140,7 +15165,8 @@
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, resultExtras,
                     requiredPermissions, OP_NONE, bOptions, serialized,
-                    sticky, -1, uid, userId, allowBackgroundActivityStarts);
+                    sticky, -1, uid, realCallingUid, realCallingPid, userId,
+                    allowBackgroundActivityStarts);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -17722,15 +17748,16 @@
         }
 
         @Override
-        public int broadcastIntentInPackage(String packageName, int uid, Intent intent,
-                String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
-                Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-                boolean sticky, int userId, boolean allowBackgroundActivityStarts) {
+        public int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
+                int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
+                int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
+                Bundle bOptions, boolean serialized, boolean sticky, int userId,
+                boolean allowBackgroundActivityStarts) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
-                        intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
-                        requiredPermission, bOptions, serialized, sticky, userId,
-                        allowBackgroundActivityStarts);
+                        realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode,
+                        resultData, resultExtras, requiredPermission, bOptions, serialized, sticky,
+                        userId, allowBackgroundActivityStarts);
             }
         }
 
@@ -17831,8 +17858,8 @@
                         | Intent.FLAG_RECEIVER_FOREGROUND
                         | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                        OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                        UserHandle.USER_ALL);
+                        OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                        Binder.getCallingPid(), UserHandle.USER_ALL);
                 if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
                     intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
@@ -17842,8 +17869,8 @@
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     }
                     broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                            OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                            UserHandle.USER_ALL);
+                            OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                            Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
 
                 // Send a broadcast to PackageInstallers if the configuration change is interesting
@@ -17858,7 +17885,7 @@
                             new String[] { android.Manifest.permission.INSTALL_PACKAGES };
                     broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
                             permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                            UserHandle.USER_ALL);
+                            Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
             }
         }
@@ -17882,7 +17909,8 @@
                 }
 
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                        OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL);
+                        OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
+                        Binder.getCallingPid(), UserHandle.USER_ALL);
             }
         }
 
@@ -17897,10 +17925,18 @@
         @Override
         public void startProcess(String processName, ApplicationInfo info,
                 boolean knownToBeDead, String hostingType, ComponentName hostingName) {
-            synchronized (ActivityManagerService.this) {
-                startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
-                        hostingType, hostingName, false /* allowWhileBooting */,
-                        false /* isolated */, true /* keepIfLarge */);
+            try {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
+                            + processName);
+                }
+                synchronized (ActivityManagerService.this) {
+                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
+                            hostingType, hostingName, false /* allowWhileBooting */,
+                            false /* isolated */, true /* keepIfLarge */);
+                }
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -18101,8 +18137,10 @@
                     if (!queue.isIdle()) {
                         final String msg = "Waiting for queue " + queue + " to become idle...";
                         pw.println(msg);
+                        pw.println(queue.describeState());
                         pw.flush();
                         Slog.v(TAG, msg);
+                        queue.cancelDeferrals();
                         idle = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index c7e4fc7..17ffd9c 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -28,6 +28,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityThread;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
@@ -410,6 +411,7 @@
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
                                 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
                                 + ": " + name);
+                        long zramFreeKbBefore = Debug.getZramFreeKb();
                         long[] rssBefore = Process.getRss(pid);
                         FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
                         fos.write(action.getBytes());
@@ -417,10 +419,12 @@
                         long[] rssAfter = Process.getRss(pid);
                         long end = SystemClock.uptimeMillis();
                         long time = end - start;
+                        long zramFreeKbAfter = Debug.getZramFreeKb();
                         EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
                                 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                 rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
-                                lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+                                lastCompactAction, lastCompactTime, msg.arg1, msg.arg2,
+                                zramFreeKbBefore, zramFreeKbAfter);
                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
                         // on every single compaction for a flag that will seldom change and the
                         // impact of reading the wrong value here is low.
@@ -429,7 +433,8 @@
                                     rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                     rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
                                     lastCompactAction, lastCompactTime, msg.arg1,
-                                    ActivityManager.processStateAmToProto(msg.arg2));
+                                    ActivityManager.processStateAmToProto(msg.arg2),
+                                    zramFreeKbBefore, zramFreeKbAfter);
                         }
                         synchronized (mAm) {
                             proc.lastCompactTime = end;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 24543b7..236797b 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -477,6 +477,10 @@
                 mStats.updateRpmStatsLocked();
             }
 
+            if ((updateFlags & UPDATE_RAIL) != 0) {
+                mStats.updateRailStatsLocked();
+            }
+
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
                     mStats.updateBluetoothStateLocked(bluetoothInfo);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0890032..4d5cb8c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -59,6 +59,7 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.os.RailStats;
 import com.android.internal.os.RpmStats;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.ParseUtils;
@@ -84,7 +85,8 @@
  */
 public final class BatteryStatsService extends IBatteryStats.Stub
         implements PowerManagerInternal.LowPowerModeListener,
-        BatteryStatsImpl.PlatformIdleStateCallback {
+        BatteryStatsImpl.PlatformIdleStateCallback,
+        BatteryStatsImpl.RailEnergyDataCallback {
     static final String TAG = "BatteryStatsService";
     static final boolean DBG = false;
 
@@ -98,6 +100,7 @@
     private native void getLowPowerStats(RpmStats rpmStats);
     private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
     private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
+    private native void getRailEnergyPowerStats(RailStats railStats);
     private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
                     .newDecoder()
                     .onMalformedInput(CodingErrorAction.REPLACE)
@@ -121,6 +124,16 @@
     }
 
     @Override
+    public void fillRailDataStats(RailStats railStats) {
+        if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats");
+        try {
+            getRailEnergyPowerStats(railStats);
+        } finally {
+            if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats");
+        }
+    }
+
+    @Override
     public String getPlatformLowPowerStats() {
         if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
         try {
@@ -177,7 +190,8 @@
                 return (umi != null) ? umi.getUserIds() : null;
             }
         };
-        mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider);
+        mStats = new BatteryStatsImpl(systemDir, handler, this,
+                this, mUserManagerUserInfoProvider);
         mWorker = new BatteryExternalStatsWorker(context, mStats);
         mStats.setExternalStatsSyncLocked(mWorker);
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
@@ -1460,7 +1474,8 @@
                                 in.unmarshall(raw, 0, raw.length);
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
-                                        null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+                                        null, mStats.mHandler, null, null,
+                                        mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpProtoLocked(
@@ -1498,7 +1513,8 @@
                                 in.unmarshall(raw, 0, raw.length);
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
-                                        null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+                                        null, mStats.mHandler, null, null,
+                                        mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 6371cd3..0b38ef9 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -65,6 +65,14 @@
             broadcasts.add(br);
         }
 
+        int size() {
+            return broadcasts.size();
+        }
+
+        boolean isEmpty() {
+            return broadcasts.isEmpty();
+        }
+
         void writeToProto(ProtoOutputStream proto, long fieldId) {
             for (BroadcastRecord br : broadcasts) {
                 br.writeToProto(proto, fieldId);
@@ -252,22 +260,48 @@
         synchronized (mLock) {
             return mCurrentBroadcast == null
                     && mOrderedBroadcasts.isEmpty()
-                    && mDeferredBroadcasts.isEmpty()
-                    && mAlarmBroadcasts.isEmpty();
+                    && isDeferralsListEmpty(mDeferredBroadcasts)
+                    && isDeferralsListEmpty(mAlarmBroadcasts);
         }
     }
 
-    /**
-     * Not quite the traditional size() measurement; includes any in-process but
-     * not yet retired active outbound broadcast.
-     */
-    public int totalUndelivered() {
-        synchronized (mLock) {
-            return mAlarmBroadcasts.size()
-                    + mDeferredBroadcasts.size()
-                    + mOrderedBroadcasts.size()
-                    + (mCurrentBroadcast == null ? 0 : 1);
+    private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
+        int pending = 0;
+        final int numEntries = list.size();
+        for (int i = 0; i < numEntries; i++) {
+            pending += list.get(i).size();
         }
+        return pending;
+    }
+
+    private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
+        return pendingInDeferralsList(list) == 0;
+    }
+
+    /**
+     * Strictly for logging, describe the currently pending contents in a human-
+     * readable way
+     */
+    public String describeStateLocked() {
+        final StringBuilder sb = new StringBuilder(128);
+        if (mCurrentBroadcast != null) {
+            sb.append("1 in flight, ");
+        }
+        sb.append(mOrderedBroadcasts.size());
+        sb.append(" ordered");
+        int n = pendingInDeferralsList(mAlarmBroadcasts);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferrals in alarm recipients");
+        }
+        n = pendingInDeferralsList(mDeferredBroadcasts);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferred");
+        }
+        return sb.toString();
     }
 
     // ----------------------------------
@@ -579,6 +613,26 @@
         }
     }
 
+    /**
+     * Cancel all current deferrals; that is, make all currently-deferred broadcasts
+     * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
+     */
+    public void cancelDeferrals() {
+        synchronized (mLock) {
+            zeroDeferralTimes(mAlarmBroadcasts);
+            zeroDeferralTimes(mDeferredBroadcasts);
+        }
+    }
+
+    private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
+        final int num = list.size();
+        for (int i = 0; i < num; i++) {
+            Deferrals d = list.get(i);
+            // Safe to do this in-place because it won't break ordering
+            d.deferUntil = d.deferredBy = 0;
+        }
+    }
+
     // ----------------------------------
 
     /**
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b137a..d9ea1da 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -898,6 +898,11 @@
         for (int i = perms.length-1; i >= 0; i--) {
             try {
                 PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
+                if (pi == null) {
+                    // a required permission that no package has actually
+                    // defined cannot be signature-required.
+                    return false;
+                }
                 if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
                         | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
                         != PermissionInfo.PROTECTION_SIGNATURE) {
@@ -923,8 +928,8 @@
 
         if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                 + mQueueName + "]: "
-                + mParallelBroadcasts.size() + " parallel broadcasts, "
-                + mDispatcher.totalUndelivered() + " ordered broadcasts");
+                + mParallelBroadcasts.size() + " parallel broadcasts; "
+                + mDispatcher.describeStateLocked());
 
         mService.updateCpuStats();
 
@@ -1822,11 +1827,24 @@
                 record.intent == null ? "" : record.intent.getAction());
     }
 
-    final boolean isIdle() {
+    boolean isIdle() {
         return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
                 && (mPendingBroadcast == null);
     }
 
+    // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
+    // be immediately deliverable.
+    void cancelDeferrals() {
+        mDispatcher.cancelDeferrals();
+    }
+
+    String describeState() {
+        synchronized (mService) {
+            return mParallelBroadcasts.size() + " parallel; "
+                    + mDispatcher.describeStateLocked();
+        }
+    }
+
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 0194624..06d0152 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -65,6 +65,8 @@
                 Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(
                 Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class);
+        sGlobalSettingToTypeMap.put(
+                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
@@ -76,6 +78,8 @@
         sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class);
         // add other global settings here...
     }
 
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index a71f6af..4b12e43 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -138,4 +138,4 @@
 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
 
 # The task is being compacted
-30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2)
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(AfterZRAMFree|2|2)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 93a71e5..4e03b72 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1692,7 +1692,8 @@
                         (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
                                 app.curAdj == ProcessList.HOME_APP_ADJ)) {
                     mAppCompact.compactAppSome(app);
-                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                                || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
                         && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
                         && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
                     mAppCompact.compactAppFull(app);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index af56352..a08c829 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -423,9 +423,9 @@
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
-                                uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
-                                requiredPermission, options, (finishedReceiver != null),
-                                false, userId,
+                                uid, callingUid, callingPid, finalIntent, resolvedType,
+                                finishedReceiver, code, null, null, requiredPermission, options,
+                                (finishedReceiver != null), false, userId,
                                 mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
                                 || allowTrampoline);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 3ea1147..376999d 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -27,6 +27,7 @@
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -108,7 +109,7 @@
         mIntent.setComponent(componentName);
         mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
                 AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
-                Process.SYSTEM_UID, mUserId);
+                Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f01305e..3a61dd9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1498,6 +1498,13 @@
                 // Also turn on CheckJNI for debuggable apps. It's quite
                 // awkward to turn on otherwise.
                 runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+
+                // Check if the developer does not want ART verification
+                if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(),
+                        android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {
+                    runtimeFlags |= Zygote.DISABLE_VERIFIER;
+                    Slog.w(TAG_PROCESSES, app + ": ART verification disabled");
+                }
             }
             // Run the app in safe mode if its manifest requests so or the
             // system is booted in safe mode.
@@ -1681,7 +1688,8 @@
     public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
         final ApplicationInfo appInfo = appZygote.getAppInfo();
         ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
-        if (zygoteProcesses.size() == 0) { // Only remove if no longer in use now
+        if (zygoteProcesses != null && zygoteProcesses.size() == 0) {
+            // Only remove if no longer in use now
             mAppZygotes.remove(appInfo.processName, appInfo.uid);
             mAppZygoteProcesses.remove(appZygote);
             mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
@@ -1703,9 +1711,16 @@
             ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
             zygoteProcesses.remove(app);
             if (zygoteProcesses.size() == 0) {
-                Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
-                msg.obj = appZygote;
-                mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
+                mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG);
+                if (app.removed) {
+                    // If we stopped this process because the package hosting it was removed,
+                    // there's no point in delaying the app zygote kill.
+                    killAppZygoteIfNeededLocked(appZygote);
+                } else {
+                    Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
+                    msg.obj = appZygote;
+                    mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
+                }
             }
         }
     }
@@ -2160,6 +2175,29 @@
         for (int i=0; i<N; i++) {
             removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
         }
+        // See if there are any app zygotes running for this packageName / UID combination,
+        // and kill it if so.
+        final ArrayList<AppZygote> zygotesToKill = new ArrayList<>();
+        for (SparseArray<AppZygote> appZygotes : mAppZygotes.getMap().values()) {
+            for (int i = 0; i < appZygotes.size(); ++i) {
+                final int appZygoteUid = appZygotes.keyAt(i);
+                if (userId != UserHandle.USER_ALL && UserHandle.getUserId(appZygoteUid) != userId) {
+                    continue;
+                }
+                if (appId >= 0 && UserHandle.getAppId(appZygoteUid) != appId) {
+                    continue;
+                }
+                final AppZygote appZygote = appZygotes.valueAt(i);
+                if (packageName != null
+                        && !packageName.equals(appZygote.getAppInfo().packageName)) {
+                    continue;
+                }
+                zygotesToKill.add(appZygote);
+            }
+        }
+        for (AppZygote appZygote : zygotesToKill) {
+            killAppZygoteIfNeededLocked(appZygote);
+        }
         mService.updateOomAdjLocked();
         return N > 0;
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 7f6648a..ac20f6c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -371,7 +371,8 @@
                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
                     new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+                    Binder.getCallingUid(), Binder.getCallingPid(), userId);
         }
 
         // We need to delay unlocking managed profiles until the parent user
@@ -471,7 +472,7 @@
                 Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
         mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,
                 null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                userId);
+                Binder.getCallingUid(), Binder.getCallingPid(), userId);
 
         if (getUserInfo(userId).isManagedProfile()) {
             UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
@@ -484,8 +485,8 @@
                                 | Intent.FLAG_RECEIVER_FOREGROUND);
                 mInjector.broadcastIntent(profileUnlockedIntent,
                         null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                        null, false, false, MY_PID, SYSTEM_UID,
-                        parent.id);
+                        null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                        Binder.getCallingPid(), parent.id);
             }
         }
 
@@ -543,7 +544,8 @@
                                 mInjector.getUserManager().makeInitialized(userInfo.id);
                             }
                         }, 0, null, null, null, AppOpsManager.OP_NONE,
-                        null, true, false, MY_PID, SYSTEM_UID, userId);
+                        null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                        Binder.getCallingPid(), userId);
             }
         }
 
@@ -573,7 +575,8 @@
                     }
                 }, 0, null, null,
                 new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+                AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+                Binder.getCallingUid(), Binder.getCallingPid(), userId);
     }
 
     int restartUser(final int userId, final boolean foreground) {
@@ -696,7 +699,8 @@
                 mInjector.broadcastIntent(stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
                         new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
-                        null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+                        null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                        Binder.getCallingPid(), UserHandle.USER_ALL);
             });
         }
     }
@@ -735,7 +739,8 @@
         mInjector.broadcastIntent(shutdownIntent,
                 null, shutdownReceiver, 0, null, null, null,
                 AppOpsManager.OP_NONE,
-                null, true, false, MY_PID, SYSTEM_UID, userId);
+                null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                Binder.getCallingPid(), userId);
     }
 
     void finishUserStopped(UserState uss) {
@@ -834,7 +839,8 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         mInjector.broadcastIntent(intent,
                 null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+                null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+                Binder.getCallingPid(), UserHandle.USER_ALL);
     }
 
     /**
@@ -950,6 +956,8 @@
 
         Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground);
 
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
         final long ident = Binder.clearCallingIdentity();
         try {
             final int oldUserId = getCurrentUserId();
@@ -1088,7 +1096,7 @@
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 mInjector.broadcastIntent(intent,
                         null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                        null, false, false, MY_PID, SYSTEM_UID, userId);
+                        null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId);
             }
 
             if (foreground) {
@@ -1111,7 +1119,8 @@
                             }
                         }, 0, null, null,
                         new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
-                        null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+                        null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
+                        UserHandle.USER_ALL);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1427,6 +1436,8 @@
     }
 
     void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
         long ident = Binder.clearCallingIdentity();
         try {
             Intent intent;
@@ -1442,7 +1453,8 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
                     mInjector.broadcastIntent(intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+                            null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
+                            profileUserId);
                 }
             }
             if (newUserId >= 0) {
@@ -1457,7 +1469,8 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
                     mInjector.broadcastIntent(intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+                            null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
+                            profileUserId);
                 }
                 intent = new Intent(Intent.ACTION_USER_SWITCHED);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -1466,8 +1479,8 @@
                 mInjector.broadcastIntent(intent,
                         null, null, 0, null, null,
                         new String[] {android.Manifest.permission.MANAGE_USERS},
-                        AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                        UserHandle.USER_ALL);
+                        AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid,
+                        callingPid, UserHandle.USER_ALL);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2107,12 +2120,14 @@
         protected int broadcastIntent(Intent intent, String resolvedType,
                 IIntentReceiver resultTo, int resultCode, String resultData,
                 Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
-                boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
+                boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
+                int realCallingPid, int userId) {
             // TODO b/64165549 Verify that mLock is not held before calling AMS methods
             synchronized (mService) {
                 return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
                         resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
-                        ordered, sticky, callingPid, callingUid, userId);
+                        ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid,
+                        userId);
             }
         }
 
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 0b6a432..bbe4ed1 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -177,13 +177,12 @@
      * Handle boot phase PHASE_ACTIVITY_MANAGER_READY.
      */
     private void onPhaseActivityManagerReady() {
+        // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades.
+        // (app uninstall/disable will be notified by RoleManager.)
         final IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageFilter.addDataScheme("package");
 
-        packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
                 packageFilter, null, mHandler);
 
@@ -256,14 +255,6 @@
                         handlePackageAddedReplacing(packageName, userId);
                     }
                     break;
-                case Intent.ACTION_PACKAGE_REMOVED:
-                    if (!replacing) {
-                        handlePackageRemoved(packageName, userId);
-                    }
-                    break;
-                case Intent.ACTION_PACKAGE_CHANGED:
-                    handlePackageChanged(packageName, userId);
-                    break;
             }
         }
     };
@@ -371,31 +362,6 @@
         }
     }
 
-    private void handlePackageRemoved(String packageName, int userId) {
-        if (DEBUG) {
-            Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName);
-        }
-        synchronized (mLock) {
-            final AppServiceFinder finder = findFinderLocked(userId, packageName);
-            if (finder != null) {
-                unbindServicesLocked(userId, finder, "package uninstall");
-            }
-        }
-    }
-
-    private void handlePackageChanged(String packageName, int userId) {
-        if (DEBUG) {
-            Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName);
-        }
-        synchronized (mLock) {
-            final AppServiceFinder finder = findFinderLocked(userId, packageName);
-            if (finder != null) {
-                unbindServicesLocked(userId, finder, "package changed");
-                bindServicesLocked(userId, finder, "package changed");
-            }
-        }
-    }
-
     private void rebindAllLocked(String reason) {
         for (int i = 0; i < mRunningUsers.size(); i++) {
             if (!mRunningUsers.valueAt(i)) {
diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index 4c5f1a1..3663518 100644
--- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -16,14 +16,10 @@
 
 package com.android.server.appbinding.finders;
 
-import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
-
 import android.Manifest.permission;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ServiceInfo;
 import android.os.Handler;
 import android.os.IBinder;
@@ -35,7 +31,8 @@
 import android.util.Slog;
 
 import com.android.internal.R;
-import com.android.internal.telephony.SmsApplication;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
 import com.android.server.appbinding.AppBindingConstants;
 
 import java.util.function.BiConsumer;
@@ -45,10 +42,15 @@
  */
 public class CarrierMessagingClientServiceFinder
         extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> {
+
+    private final RoleManager mRoleManager;
+
     public CarrierMessagingClientServiceFinder(Context context,
             BiConsumer<AppServiceFinder, Integer> listener,
             Handler callbackHandler) {
         super(context, listener, callbackHandler);
+
+        mRoleManager = context.getSystemService(RoleManager.class);
     }
 
     @Override
@@ -84,9 +86,8 @@
 
     @Override
     public String getTargetPackage(int userId) {
-        final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
-                mContext, /* updateIfNeeded= */ true, userId);
-        String ret = cn == null ? null : cn.getPackageName();
+        final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser(
+                RoleManager.ROLE_SMS, UserHandle.of(userId)));
 
         if (DEBUG) {
             Slog.d(TAG, "getTargetPackage()=" + ret);
@@ -97,9 +98,8 @@
 
     @Override
     public void startMonitoring() {
-        final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
-        mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter,
-                /* permission= */ null, mHandler);
+        mRoleManager.addOnRoleHoldersChangedListenerAsUser(
+                BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
     }
 
     @Override
@@ -118,12 +118,9 @@
         return constants.SMS_APP_BIND_FLAGS;
     }
 
-    private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
-                mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId());
-            }
+    private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> {
+        if (RoleManager.ROLE_SMS.equals(role)) {
+            mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
         }
     };
 }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 0e33090..70c28a8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -38,6 +38,7 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.content.BroadcastReceiver;
@@ -1024,25 +1025,29 @@
 
     @Override
     public void getHistoricalOps(int uid, @NonNull String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
             @NonNull RemoteCallback callback) {
-        Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
-                "uid must be " + Process.INVALID_UID + " or non negative");
-        Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
-                "beginTimeMillis must be non negative and lesser than endTimeMillis");
+        // Use the builder to validate arguments.
+        final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
+                beginTimeMillis, endTimeMillis)
+                .setUid(uid)
+                .setPackageName(packageName)
+                .setOpNames(opNames)
+                .build();
         Preconditions.checkNotNull(callback, "callback cannot be null");
-        checkValidOpsOrNull(opNames);
 
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
 
+        final String[] opNamesArray = (opNames != null)
+                ? opNames.toArray(new String[opNames.size()]) : null;
         if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) {
             // TODO (bug:122218838): Remove once the feature fully enabled.
-            getHistoricalPackagesOpsCompat(uid, packageName, opNames, beginTimeMillis,
+            getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis,
                     endTimeMillis, callback);
         } else {
             // Must not hold the appops lock
-            mHistoricalRegistry.getHistoricalOps(uid, packageName, opNames,
+            mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
                     beginTimeMillis, endTimeMillis, callback);
         }
     }
@@ -1101,20 +1106,25 @@
 
     @Override
     public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
             @NonNull RemoteCallback callback) {
-        Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
-                "uid must be " + Process.INVALID_UID + " or non negative");
-        Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
-                "beginTimeMillis must be non negative and lesser than endTimeMillis");
+        // Use the builder to validate arguments.
+        final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
+                beginTimeMillis, endTimeMillis)
+                .setUid(uid)
+                .setPackageName(packageName)
+                .setOpNames(opNames)
+                .build();
         Preconditions.checkNotNull(callback, "callback cannot be null");
-        checkValidOpsOrNull(opNames);
 
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
 
+        final String[] opNamesArray = (opNames != null)
+                ? opNames.toArray(new String[opNames.size()]) : null;
+
         // Must not hold the appops lock
-        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNames,
+        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray,
                 beginTimeMillis, endTimeMillis, callback);
     }
 
@@ -4266,16 +4276,6 @@
         return packageNames;
     }
 
-    private static void checkValidOpsOrNull(String[] opNames) {
-        if (opNames != null) {
-            for (String opName : opNames) {
-                if (AppOpsManager.strOpToOp(opName) == AppOpsManager.OP_NONE) {
-                    throw new IllegalArgumentException("Unknown op: " + opName);
-                }
-            }
-        }
-    }
-
     private final class ClientRestrictionState implements DeathRecipient {
         private final IBinder token;
         SparseArray<boolean[]> perUserRestrictions;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 9a1d7bf..47c9b86 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -104,8 +104,7 @@
 
     @Override
     public void onSwitchUser(int userId) {
-        cancelAndUnbindLocked(peekUserStateLocked(userId),
-                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+        cancelAndUnbindLocked(peekUserStateLocked(userId));
     }
 
     /** Resolves and sets up the attention service if it had not been done yet. */
@@ -152,7 +151,8 @@
         }
 
         synchronized (mLock) {
-            unbindAfterTimeoutLocked();
+            final long now = SystemClock.uptimeMillis();
+            freeIfInactiveLocked();
 
             final UserState userState = getOrCreateCurrentUserStateLocked();
             // lazily start the service, which should be very lightweight to start
@@ -172,7 +172,7 @@
                 try {
                     // throttle frequent requests
                     final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache;
-                    if (attentionCheckCache != null && SystemClock.uptimeMillis()
+                    if (attentionCheckCache != null && now
                             < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) {
                         callback.onSuccess(requestCode, attentionCheckCache.mResult,
                                 attentionCheckCache.mTimestamp);
@@ -190,6 +190,7 @@
                                 userState.mAttentionCheckCache = new AttentionCheckCache(
                                         SystemClock.uptimeMillis(), result,
                                         timestamp);
+                                userState.mCurrentAttentionCheckIsFulfilled = true;
                             }
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     result);
@@ -198,14 +199,10 @@
                         @Override
                         public void onFailure(int requestCode, int error) {
                             callback.onFailure(requestCode, error);
+                            userState.mCurrentAttentionCheckIsFulfilled = true;
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     error);
                         }
-
-                        @Override
-                        public IBinder asBinder() {
-                            return null;
-                        }
                     });
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService");
@@ -219,7 +216,10 @@
     /** Cancels the specified attention check. */
     public void cancelAttentionCheck(int requestCode) {
         synchronized (mLock) {
-            final UserState userState = getOrCreateCurrentUserStateLocked();
+            final UserState userState = peekCurrentUserStateLocked();
+            if (userState == null) {
+                return;
+            }
             if (userState.mService == null) {
                 if (userState.mPendingAttentionCheck != null
                         && userState.mPendingAttentionCheck.mRequestCode == requestCode) {
@@ -236,8 +236,12 @@
     }
 
     @GuardedBy("mLock")
-    private void unbindAfterTimeoutLocked() {
-        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED,
+    private void freeIfInactiveLocked() {
+        // If we are called here, it means someone used the API again - reset the timer then.
+        mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
+
+        // Schedule resources cleanup if no one calls the API again.
+        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION,
                 CONNECTION_TTL_MILLIS);
     }
 
@@ -264,12 +268,14 @@
     }
 
     @GuardedBy("mLock")
-    UserState peekCurrentUserStateLocked() {
+    @Nullable
+    private UserState peekCurrentUserStateLocked() {
         return peekUserStateLocked(ActivityManager.getCurrentUser());
     }
 
     @GuardedBy("mLock")
-    UserState peekUserStateLocked(int userId) {
+    @Nullable
+    private UserState peekUserStateLocked(int userId) {
         return mUserStates.get(userId);
     }
 
@@ -406,6 +412,8 @@
         @GuardedBy("mLock")
         int mCurrentAttentionCheckRequestCode;
         @GuardedBy("mLock")
+        boolean mCurrentAttentionCheckIsFulfilled;
+        @GuardedBy("mLock")
         PendingAttentionCheck mPendingAttentionCheck;
 
         @GuardedBy("mLock")
@@ -501,7 +509,7 @@
     }
 
     private class AttentionHandler extends Handler {
-        private static final int CONNECTION_EXPIRED = 1;
+        private static final int CHECK_CONNECTION_EXPIRATION = 1;
         private static final int ATTENTION_CHECK_TIMEOUT = 2;
 
         AttentionHandler() {
@@ -511,19 +519,26 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 // Do not occupy resources when not in use - unbind proactively.
-                case CONNECTION_EXPIRED: {
+                case CHECK_CONNECTION_EXPIRATION: {
                     for (int i = 0; i < mUserStates.size(); i++) {
-                        cancelAndUnbindLocked(mUserStates.valueAt(i),
-                                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                        cancelAndUnbindLocked(mUserStates.valueAt(i));
                     }
-
                 }
                 break;
 
                 // Callee is no longer interested in the attention check result - cancel.
                 case ATTENTION_CHECK_TIMEOUT: {
-                    cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                            AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                    synchronized (mLock) {
+                        final UserState userState = peekCurrentUserStateLocked();
+                        if (userState != null) {
+                            // If not called back already.
+                            if (!userState.mCurrentAttentionCheckIsFulfilled) {
+                                cancel(userState,
+                                        AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                            }
+
+                        }
+                    }
                 }
                 break;
 
@@ -533,25 +548,29 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void cancelAndUnbindLocked(UserState userState,
-            @AttentionFailureCodes int failureCode) {
-        synchronized (mLock) {
-            if (userState != null && userState.mService != null) {
-                try {
-                    userState.mService.cancelAttentionCheck(
-                            userState.mCurrentAttentionCheckRequestCode);
-                } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Unable to cancel attention check");
-                }
-
-                if (userState.mPendingAttentionCheck != null) {
-                    userState.mPendingAttentionCheck.cancel(failureCode);
-                }
-                mContext.unbindService(userState.mConnection);
-                userState.mConnection.cleanupService();
-                mUserStates.remove(userState.mUserId);
+    private void cancel(UserState userState, @AttentionFailureCodes int failureCode) {
+        if (userState != null && userState.mService != null) {
+            try {
+                userState.mService.cancelAttentionCheck(
+                        userState.mCurrentAttentionCheckRequestCode);
+            } catch (RemoteException e) {
+                Slog.e(LOG_TAG, "Unable to cancel attention check");
             }
+
+            if (userState.mPendingAttentionCheck != null) {
+                userState.mPendingAttentionCheck.cancel(failureCode);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void cancelAndUnbindLocked(UserState userState) {
+        synchronized (mLock) {
+            cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN);
+
+            mContext.unbindService(userState.mConnection);
+            userState.mConnection.cleanupService();
+            mUserStates.remove(userState.mUserId);
         }
     }
 
@@ -563,8 +582,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
-                cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                        AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                cancelAndUnbindLocked(peekCurrentUserStateLocked());
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6df60d6..9af57da 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -280,9 +280,9 @@
                     AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
             sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
                     AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
-            // Un-mute ringtone stream volume
-            mAudioService.setUpdateRingerModeServiceInt();
         }
+        // Un-mute ringtone stream volume
+        mAudioService.postUpdateRingerModeServiceInt();
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6643d4..d902201 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -248,6 +248,7 @@
     private static final int MSG_NOTIFY_VOL_EVENT = 22;
     private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
     private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
+    private static final int MSG_UPDATE_RINGER_MODE = 25;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -753,6 +754,7 @@
         intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
         intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
 
         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
@@ -2720,7 +2722,11 @@
         }
     }
 
-    /*package*/ void setUpdateRingerModeServiceInt() {
+    /*package*/ void postUpdateRingerModeServiceInt() {
+        sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0);
+    }
+
+    private void onUpdateRingerModeServiceInt() {
         setRingerModeInt(getRingerModeInternal(), false);
     }
 
@@ -3217,6 +3223,21 @@
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
         }
+
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mSetModeDeathHandlers) {
+                for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                    if (h.getMode() == AudioSystem.MODE_IN_CALL) {
+                        Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+                                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                        return;
+                    }
+                }
+            }
+        }
+
         // for logging only
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
@@ -4944,6 +4965,10 @@
                 case MSG_ENABLE_SURROUND_FORMATS:
                     onEnableSurroundFormats((ArrayList<Integer>) msg.obj);
                     break;
+
+                case MSG_UPDATE_RINGER_MODE:
+                    onUpdateRingerModeServiceInt();
+                    break;
             }
         }
     }
@@ -5159,6 +5184,20 @@
             } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) ||
                     action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) {
                 handleAudioEffectBroadcast(context, intent);
+            } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
+                final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+                final String[] suspendedPackages =
+                        intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (suspendedPackages == null || suspendedUids == null
+                        || suspendedPackages.length != suspendedUids.length) {
+                    return;
+                }
+                for (int i = 0; i < suspendedUids.length; i++) {
+                    if (!TextUtils.isEmpty(suspendedPackages[i])) {
+                        mMediaFocusControl.noFocusForSuspendedApp(
+                                suspendedPackages[i], suspendedUids[i]);
+                    }
+                }
             }
         }
     } // end class AudioServiceBroadcastReceiver
@@ -5323,6 +5362,11 @@
             }
         }
 
+        if (callingPackageName == null || clientId == null || aa == null) {
+            Log.e(TAG, "Invalid null parameter to request audio focus");
+            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+        }
+
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
                 clientId, callingPackageName, flags, sdk,
                 forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 99f0840..db55138 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -45,8 +45,8 @@
     private AudioFocusDeathHandler mDeathHandler; // may be null
     private IAudioFocusDispatcher mFocusDispatcher; // may be null
     private final IBinder mSourceRef; // may be null
-    private final String mClientId;
-    private final String mPackageName;
+    private final @NonNull String mClientId;
+    private final @NonNull String mPackageName;
     private final int mCallingUid;
     private final MediaFocusControl mFocusController; // never null
     private final int mSdkTarget;
@@ -72,7 +72,7 @@
     /**
      * the audio attributes associated with the focus request
      */
-    private final AudioAttributes mAttributes;
+    private final @NonNull AudioAttributes mAttributes;
 
     /**
      * Class constructor
@@ -87,9 +87,10 @@
      * @param uid
      * @param ctlr cannot be null
      */
-    FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
-            IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
-            String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) {
+    FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags,
+            IAudioFocusDispatcher afl, IBinder source, @NonNull String id,
+            AudioFocusDeathHandler hdlr, @NonNull String pn, int uid,
+            @NonNull MediaFocusControl ctlr, int sdk) {
         mAttributes = aa;
         mFocusDispatcher = afl;
         mSourceRef = source;
@@ -124,11 +125,7 @@
     }
 
     boolean hasSameClient(String otherClient) {
-        try {
-            return mClientId.compareTo(otherClient) == 0;
-        } catch (NullPointerException e) {
-            return false;
-        }
+        return mClientId.compareTo(otherClient) == 0;
     }
 
     boolean isLockedFocusOwner() {
@@ -143,12 +140,8 @@
         return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
     }
 
-    boolean hasSamePackage(String pack) {
-        try {
-            return mPackageName.compareTo(pack) == 0;
-        } catch (NullPointerException e) {
-            return false;
-        }
+    boolean hasSamePackage(@NonNull String pack) {
+        return mPackageName.compareTo(pack) == 0;
     }
 
     boolean hasSameUid(int uid) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index d023bd7..b4bbbc7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -24,7 +24,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.IAudioFocusDispatcher;
-import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.IAudioPolicyCallback;
 import android.os.Binder;
 import android.os.Build;
@@ -35,6 +34,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -44,7 +44,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.Stack;
-import java.text.DateFormat;
 
 /**
  * @hide
@@ -138,6 +137,30 @@
     private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
             "focus commands as seen by MediaFocusControl");
 
+    /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) {
+        synchronized (mAudioFocusLock) {
+            final Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
+            List<String> clientsToRemove = new ArrayList<>();
+            while (stackIterator.hasNext()) {
+                final FocusRequester focusOwner = stackIterator.next();
+                if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
+                    clientsToRemove.add(focusOwner.getClientId());
+                    mEventLogger.log((new AudioEventLogger.StringEvent(
+                            "focus owner:" + focusOwner.getClientId()
+                                    + " in uid:" + uid + " pack: " + packageName
+                                    + " getting AUDIOFOCUS_LOSS due to app suspension"))
+                            .printLog(TAG));
+                    // make the suspended app lose focus through its focus listener (if any)
+                    focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+                }
+            }
+            for (String clientToRemove : clientsToRemove) {
+                // update the stack but don't signal the change.
+                removeFocusStackEntry(clientToRemove, false, true);
+            }
+        }
+    }
+
     /**
      * Discard the current audio focus owner.
      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
@@ -688,9 +711,9 @@
     }
 
     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
-    protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
-            int sdk, boolean forceDuck) {
+    protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
+            IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
+            int flags, int sdk, boolean forceDuck) {
         mEventLogger.log((new AudioEventLogger.StringEvent(
                 "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
                     + "/" + Binder.getCallingPid()
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index bd4acdb..7d4ac59 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -34,7 +34,7 @@
     private long mOpId;
 
     public abstract int handleFailedAttempt();
-    public abstract void resetFailedAttempts();
+    public void resetFailedAttempts() {}
 
     public static final int LOCKOUT_NONE = 0;
     public static final int LOCKOUT_TIMED = 1;
@@ -42,6 +42,11 @@
 
     private final boolean mRequireConfirmation;
 
+    // We need to track this state since it's possible for applications to request for
+    // authentication while the device is already locked out. In that case, the client is created
+    // but not started yet. The user shouldn't receive the error haptics in this case.
+    private boolean mStarted;
+
     /**
      * This method is called when authentication starts.
      */
@@ -53,6 +58,11 @@
      */
     public abstract void onStop();
 
+    /**
+     * @return true if the framework should handle lockout.
+     */
+    public abstract boolean shouldFrameworkHandleLockout();
+
     public AuthenticationClient(Context context, Metrics metrics,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -91,6 +101,23 @@
     }
 
     @Override
+    public boolean onError(long deviceId, int error, int vendorCode) {
+        if (!shouldFrameworkHandleLockout()) {
+            switch (error) {
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+                    if (mStarted) {
+                        vibrateError();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        return super.onError(deviceId, error, vendorCode);
+    }
+
+    @Override
     public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
         super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(),
@@ -113,7 +140,9 @@
                     vibrateSuccess();
                 }
                 result = true;
-                resetFailedAttempts();
+                if (shouldFrameworkHandleLockout()) {
+                    resetFailedAttempts();
+                }
                 onStop();
 
                 final byte[] byteToken = new byte[token.size()];
@@ -147,9 +176,10 @@
                 if (listener != null) {
                     vibrateError();
                 }
+
                 // Allow system-defined limit of number of attempts before giving up
                 final int lockoutMode = handleFailedAttempt();
-                if (lockoutMode != LOCKOUT_NONE) {
+                if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
                     Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
                             + lockoutMode + ")");
                     stop(false);
@@ -170,7 +200,7 @@
                         }
                     }
                 }
-                result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+                result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
             }
         } catch (RemoteException e) {
             Slog.e(getLogTag(), "Remote exception", e);
@@ -184,6 +214,7 @@
      */
     @Override
     public int start() {
+        mStarted = true;
         onStart();
         try {
             final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
@@ -209,6 +240,8 @@
             return 0;
         }
 
+        mStarted = false;
+
         onStop();
 
         try {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index bca84f7..ddd416e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -273,16 +273,13 @@
          */
         private static final int STATE_AUTH_STARTED = 2;
         /**
-         * Authentication is paused, waiting for the user to press "try again" button. Since the
-         * try again button requires us to cancel authentication, this represents the state where
-         * ERROR_CANCELED is not received yet.
+         * Authentication is paused, waiting for the user to press "try again" button. Only
+         * passive modalities such as Face or Iris should have this state. Note that for passive
+         * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
+         * fingerprint.
          */
         private static final int STATE_AUTH_PAUSED = 3;
         /**
-         * Same as above, except the ERROR_CANCELED has been received.
-         */
-        private static final int STATE_AUTH_PAUSED_CANCELED = 4;
-        /**
          * Authentication is successful, but we're waiting for the user to press "confirm" button.
          */
         private static final int STATE_AUTH_PENDING_CONFIRM = 5;
@@ -457,11 +454,6 @@
                         // Pause authentication. onBiometricAuthenticated(false) causes the
                         // dialog to show a "try again" button for passive modalities.
                         mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
-                        // Cancel authentication. Skip the token/package check since we are
-                        // cancelling from system server. The interface is permission protected so
-                        // this is fine.
-                        cancelInternal(null /* token */, null /* package */,
-                                false /* fromClient */);
                     }
 
                     mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
@@ -507,24 +499,15 @@
                                     }
                                 }, BiometricPrompt.HIDE_DIALOG_DELAY);
                             }
-                        } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
-                            if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                    && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
-                                // Skip the first ERROR_CANCELED message when this happens, since
-                                // "try again" requires us to cancel authentication but keep
-                                // the prompt showing.
-                                mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
-                            } else {
-                                // In the "try again" state, we should forward canceled errors to
-                                // the client and and clean up.
-                                mCurrentAuthSession.mClientReceiver.onError(error, message);
-                                mStatusBarService.onBiometricError(message);
-                                mActivityTaskManager.unregisterTaskStackListener(
-                                        mTaskStackListener);
-                                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                                mCurrentAuthSession = null;
-                            }
+                        } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
+                            // In the "try again" state, we should forward canceled errors to
+                            // the client and and clean up.
+                            mCurrentAuthSession.mClientReceiver.onError(error, message);
+                            mStatusBarService.onBiometricError(message);
+                            mActivityTaskManager.unregisterTaskStackListener(
+                                    mTaskStackListener);
+                            mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                            mCurrentAuthSession = null;
                         } else {
                             Slog.e(TAG, "Impossible session error state: "
                                     + mCurrentAuthSession.mState);
@@ -705,8 +688,7 @@
 
             if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
                 final boolean continuing = mCurrentAuthSession != null &&
-                        (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED);
+                        (mCurrentAuthSession.mState == STATE_AUTH_PAUSED);
 
                 mCurrentAuthSession = mPendingAuthSession;
                 mPendingAuthSession = null;
@@ -1029,7 +1011,7 @@
         }
 
         @Override // Binder call
-        public void resetTimeout(byte[] token) {
+        public void resetLockout(byte[] token) {
             checkInternalPermission();
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -1037,7 +1019,7 @@
                     mFingerprintService.resetTimeout(token);
                 }
                 if (mFaceService != null) {
-                    mFaceService.resetTimeout(token);
+                    mFaceService.resetLockout(token);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 9e0f2fc..92a8d93 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -20,17 +20,12 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -54,14 +49,11 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
 import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
-import com.android.server.biometrics.fingerprint.FingerprintService;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -80,38 +72,36 @@
 
     protected static final boolean DEBUG = true;
 
+    private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
     private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
     private static final int MSG_USER_SWITCHING = 10;
-    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
     private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
 
     private final Context mContext;
     private final String mKeyguardPackage;
-    private final SparseBooleanArray mTimedLockoutCleared;
-    private final SparseIntArray mFailedAttempts;
     private final IActivityTaskManager mActivityTaskManager;
-    private final AlarmManager mAlarmManager;
     private final PowerManager mPowerManager;
     private final UserManager mUserManager;
     private final MetricsLogger mMetricsLogger;
     private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
     private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
-    private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
     private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
 
     protected final IStatusBarService mStatusBarService;
     protected final Map<Integer, Long> mAuthenticatorIds =
             Collections.synchronizedMap(new HashMap<>());
-    protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
-            new ResetFailedAttemptsForUserRunnable();
     protected final AppOpsManager mAppOps;
     protected final H mHandler = new H();
 
+    private final IBinder mToken = new Binder(); // Used for internal enumeration
+    private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
+
     private IBiometricService mBiometricService;
     private ClientMonitor mCurrentClient;
     private ClientMonitor mPendingClient;
     private PerformanceStats mPerformanceStats;
     protected int mCurrentUserId = UserHandle.USER_NULL;
+    protected long mHalDeviceId;
     // Tracks if the current authentication makes use of CryptoObjects.
     protected boolean mIsCrypto;
     // Normal authentications are tracked by mPerformanceMap.
@@ -135,23 +125,16 @@
     protected abstract String getTag();
 
     /**
+     * @return wrapper for the HAL
+     */
+    protected abstract DaemonWrapper getDaemonWrapper();
+
+    /**
      * @return the biometric utilities for a specific implementation.
      */
     protected abstract BiometricUtils getBiometricUtils();
 
     /**
-     * @return the number of failed attempts after which the user will be temporarily locked out
-     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
-     */
-    protected abstract int getFailedAttemptsLockoutTimed();
-
-    /**
-     * @return the number of failed attempts after which the user will be permanently locked out
-     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
-     */
-    protected abstract int getFailedAttemptsLockoutPermanent();
-
-    /**
      * @return the metrics constants for a biometric implementation.
      */
     protected abstract Metrics getMetrics();
@@ -186,13 +169,6 @@
     protected abstract long getHalDeviceId();
 
     /**
-     * This method is called when the user switches. Implementations should probably notify the
-     * HAL.
-     * @param userId
-     */
-    protected abstract void handleUserSwitching(int userId);
-
-    /**
      * @param userId
      * @return Returns true if the user has any enrolled biometrics.
      */
@@ -215,6 +191,9 @@
      */
     protected abstract boolean checkAppOps(int uid, String opPackageName);
 
+    protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
+            int userId);
+
     /**
      * Notifies clients of any change in the biometric state (active / idle). This is mainly for
      * Fingerprint navigation gestures.
@@ -224,6 +203,11 @@
 
     protected abstract int statsModality();
 
+    /**
+     * @return one of the AuthenticationClient LOCKOUT constants
+     */
+    protected abstract int getLockoutMode();
+
     protected abstract class AuthenticationClientImpl extends AuthenticationClient {
 
         // Used to check if the public API that was invoked was from FingerprintManager. Only
@@ -271,21 +255,12 @@
         }
 
         @Override
-        public void resetFailedAttempts() {
-            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                    ActivityManager.getCurrentUser());
-        }
-
-        @Override
         public void notifyUserActivity() {
             userActivity();
         }
 
         @Override
         public int handleFailedAttempt() {
-            final int currentUser = ActivityManager.getCurrentUser();
-            mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
-            mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
             final int lockoutMode = getLockoutMode();
             if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
                 mPerformanceStats.permanentLockout++;
@@ -295,7 +270,6 @@
 
             // Failing multiple times will continue to push out the lockout time
             if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
-                scheduleLockoutResetForUser(currentUser);
                 return lockoutMode;
             }
             return AuthenticationClient.LOCKOUT_NONE;
@@ -319,40 +293,106 @@
         }
     }
 
-    protected abstract class RemovalClientImpl extends RemovalClient {
-        private boolean mShouldNotify;
-
-        public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+    /**
+     * An internal class to help clean up unknown templates in HAL and Framework
+     */
+    private final class InternalRemovalClient extends RemovalClient {
+        InternalRemovalClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int templateId, int groupId, int userId,
                 boolean restricted, String owner) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+            super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
                     userId, restricted, owner, getBiometricUtils());
         }
 
-        public void setShouldNotifyUserActivity(boolean shouldNotify) {
-            mShouldNotify = shouldNotify;
-        }
-
         @Override
-        public void notifyUserActivity() {
-            if (mShouldNotify) {
-                userActivity();
-            }
+        protected int statsModality() {
+            return BiometricServiceBase.this.statsModality();
         }
     }
 
-    protected abstract class EnumerateClientImpl extends EnumerateClient {
+    /**
+     * Internal class to help clean up unknown templates in the HAL and Framework
+     */
+    private final class InternalEnumerateClient extends EnumerateClient {
 
-        public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int groupId, int userId,
-                boolean restricted, String owner) {
+        private BiometricUtils mUtils;
+        // List of templates that are known to the Framework. Remove from this list when enumerate
+        // returns a template that contains a match.
+        private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
+        // List of templates to remove from the HAL
+        private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
+
+        InternalEnumerateClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int groupId, int userId, boolean restricted,
+                String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
+                BiometricUtils utils) {
             super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
                     restricted, owner);
+            mEnrolledList = enrolledList;
+            mUtils = utils;
+        }
+
+        private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
+            if (identifier == null) {
+                return;
+            }
+            Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
+            boolean matched = false;
+            for (int i = 0; i < mEnrolledList.size(); i++) {
+                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
+                    mEnrolledList.remove(i);
+                    matched = true;
+                    break;
+                }
+            }
+
+            // TemplateId 0 means no templates in HAL
+            if (!matched && identifier.getBiometricId() != 0) {
+                mUnknownHALTemplates.add(identifier);
+            }
+            Slog.v(getTag(), "Matched: " + matched);
+        }
+
+        private void doTemplateCleanup() {
+            if (mEnrolledList == null) {
+                return;
+            }
+
+            // At this point, mEnrolledList only contains templates known to the framework and
+            // not the HAL.
+            for (int i = 0; i < mEnrolledList.size(); i++) {
+                BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
+                Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
+                        + identifier.getBiometricId() + " "
+                        + identifier.getName());
+                mUtils.removeBiometricForUser(getContext(),
+                        getTargetUserId(), identifier.getBiometricId());
+                StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        statsModality(),
+                        BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
+            }
+            mEnrolledList.clear();
+        }
+
+        public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
+            return mUnknownHALTemplates;
         }
 
         @Override
-        public void notifyUserActivity() {
-            userActivity();
+        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+                int remaining) {
+            handleEnumeratedTemplate(identifier);
+            if (remaining == 0) {
+                doTemplateCleanup();
+            }
+            return remaining == 0;
+        }
+
+        @Override
+        protected int statsModality() {
+            return BiometricServiceBase.this.statsModality();
         }
     }
 
@@ -429,13 +469,14 @@
      * subclasses.
      */
     protected interface DaemonWrapper {
-        int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+        int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
         int authenticate(long operationId, int groupId) throws RemoteException;
         int cancel() throws RemoteException;
         int remove(int groupId, int biometricId) throws RemoteException;
         int enumerate() throws RemoteException;
-        int enroll(byte[] cryptoToken, int groupId, int timeout,
+        int enroll(byte[] token, int groupId, int timeout,
                 ArrayList<Integer> disabledFeatures) throws RemoteException;
+        void resetLockout(byte[] token) throws RemoteException;
     }
 
     /**
@@ -506,24 +547,7 @@
         }
     }
 
-    private final class LockoutReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
-            if (getLockoutResetIntent().equals(intent.getAction())) {
-                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
-                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
-            }
-        }
-    }
 
-    private final class ResetFailedAttemptsForUserRunnable implements Runnable {
-        @Override
-        public void run() {
-            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                    ActivityManager.getCurrentUser());
-        }
-    }
 
     private final class LockoutResetMonitor implements IBinder.DeathRecipient {
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
@@ -583,6 +607,19 @@
     }
 
     /**
+     * Container for enumerated templates. Used to keep track when cleaning up unknown
+     * templates.
+     */
+    private final class UserTemplate {
+        final BiometricAuthenticator.Identifier mIdentifier;
+        final int mUserId;
+        UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
+            this.mIdentifier = identifier;
+            this.mUserId = userId;
+        }
+    }
+
+    /**
      * Initializes the system service.
      * <p>
      * Subclasses must define a single argument constructor that accepts the context
@@ -599,16 +636,11 @@
         mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
                 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mTimedLockoutCleared = new SparseBooleanArray();
-        mFailedAttempts = new SparseIntArray();
         mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
                 Context.ACTIVITY_TASK_SERVICE)).getService();
         mPowerManager = mContext.getSystemService(PowerManager.class);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
         mUserManager = UserManager.get(mContext);
         mMetricsLogger = new MetricsLogger();
-        mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
-                getLockoutBroadcastPermission(), null /* handler */);
     }
 
     @Override
@@ -688,6 +720,11 @@
         if (DEBUG) Slog.v(getTag(), "handleError(client="
                 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
 
+        if (client instanceof InternalRemovalClient
+                || client instanceof InternalEnumerateClient) {
+            clearEnumerateState();
+        }
+
         if (client != null && client.onError(deviceId, error, vendorCode)) {
             removeClient(client);
         }
@@ -721,6 +758,39 @@
                 updateActiveGroup(userId, null);
             }
         }
+
+        if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
+            startCleanupUnknownHALTemplates();
+        } else if (client instanceof InternalRemovalClient) {
+            clearEnumerateState();
+        }
+    }
+
+    protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
+        ClientMonitor client = getCurrentClient();
+
+        client.onEnumerationResult(identifier, remaining);
+
+        // All templates in the HAL for this user were enumerated
+        if (remaining == 0) {
+            if (client instanceof InternalEnumerateClient) {
+                List<BiometricAuthenticator.Identifier> unknownHALTemplates =
+                        ((InternalEnumerateClient) client).getUnknownHALTemplates();
+
+                if (!unknownHALTemplates.isEmpty()) {
+                    Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
+                            + " templates for deletion");
+                }
+                for (int i = 0; i < unknownHALTemplates.size(); i++) {
+                    mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
+                            client.getTargetUserId()));
+                }
+                removeClient(client);
+                startCleanupUnknownHALTemplates();
+            } else {
+                removeClient(client);
+            }
+        }
     }
 
     /**
@@ -832,13 +902,13 @@
         });
     }
 
-    protected void removeInternal(RemovalClientImpl client) {
+    protected void removeInternal(RemovalClient client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
     }
 
-    protected void enumerateInternal(EnumerateClientImpl client) {
+    protected void enumerateInternal(EnumerateClient client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
@@ -919,19 +989,6 @@
         return mKeyguardPackage.equals(clientPackage);
     }
 
-    protected int getLockoutMode() {
-        final int currentUser = ActivityManager.getCurrentUser();
-        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
-        if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
-            return AuthenticationClient.LOCKOUT_PERMANENT;
-        } else if (failedAttempts > 0 &&
-                mTimedLockoutCleared.get(currentUser, false) == false
-                && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
-            return AuthenticationClient.LOCKOUT_TIMED;
-        }
-        return AuthenticationClient.LOCKOUT_NONE;
-    }
-
     private boolean isForegroundActivity(int uid, int pid) {
         try {
             List<ActivityManager.RunningAppProcessInfo> procs =
@@ -965,10 +1022,10 @@
                     currentClient.getOwnerString());
             // This check only matters for FingerprintService, since enumerate may call back
             // multiple times.
-            if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
-                    currentClient instanceof FingerprintService.RemovalClientImpl) {
+            if (currentClient instanceof InternalEnumerateClient
+                    || currentClient instanceof InternalRemovalClient) {
                 // This condition means we're currently running internal diagnostics to
-                // remove extra fingerprints in the hardware and/or the software
+                // remove extra templates in the hardware and/or the software
                 // TODO: design an escape hatch in case client never finishes
                 if (newClient != null) {
                     Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
@@ -1124,16 +1181,75 @@
         return mAuthenticatorIds.getOrDefault(userId, 0L);
     }
 
-    private void scheduleLockoutResetForUser(int userId) {
-        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
-                getLockoutResetIntentForUser(userId));
+    /**
+     * This method should be called upon connection to the daemon, and when user switches.
+     * @param userId
+     */
+    protected void doTemplateCleanupForUser(int userId) {
+        if (CLEANUP_UNKNOWN_TEMPLATES) {
+            enumerateUser(userId);
+        }
     }
 
-    private PendingIntent getLockoutResetIntentForUser(int userId) {
-        return PendingIntent.getBroadcast(mContext, userId,
-                new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+    private void clearEnumerateState() {
+        if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
+        mUnknownHALTemplates.clear();
+    }
+
+    /**
+     * Remove unknown templates from HAL
+     */
+    private void startCleanupUnknownHALTemplates() {
+        if (!mUnknownHALTemplates.isEmpty()) {
+            UserTemplate template = mUnknownHALTemplates.get(0);
+            mUnknownHALTemplates.remove(template);
+            boolean restricted = !hasPermission(getManageBiometricPermission());
+            InternalRemovalClient client = new InternalRemovalClient(getContext(),
+                    getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
+                    template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
+                    restricted, getContext().getPackageName());
+            removeInternal(client);
+            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    statsModality(),
+                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
+        } else {
+            clearEnumerateState();
+        }
+    }
+
+    private void enumerateUser(int userId) {
+        if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
+
+        final boolean restricted = !hasPermission(getManageBiometricPermission());
+        final List<? extends BiometricAuthenticator.Identifier> enrolledList =
+                getEnrolledTemplates(userId);
+
+        InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
+                getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
+                userId, restricted, getContext().getOpPackageName(), enrolledList,
+                getBiometricUtils());
+        enumerateInternal(client);
+    }
+
+    /**
+     * This method is called when the user switches. Implementations should probably notify the
+     * HAL.
+     */
+    protected void handleUserSwitching(int userId) {
+        if (getCurrentClient() instanceof InternalRemovalClient
+                || getCurrentClient() instanceof InternalEnumerateClient) {
+            Slog.w(getTag(), "User switched while performing cleanup");
+            removeClient(getCurrentClient());
+            clearEnumerateState();
+        }
+        updateActiveGroup(userId, null);
+        doTemplateCleanupForUser(userId);
+    }
+
+    protected void notifyLockoutResetMonitors() {
+        for (int i = 0; i < mLockoutMonitors.size(); i++) {
+            mLockoutMonitors.get(i).sendLockoutReset();
+        }
     }
 
     private void userActivity() {
@@ -1169,25 +1285,6 @@
         return userId;
     }
 
-    // Attempt counter should only be cleared when Keyguard goes away or when
-    // a biometric is successfully authenticated.
-    private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
-        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
-            Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
-        }
-        if (clearAttemptCounter) {
-            mFailedAttempts.put(userId, 0);
-        }
-        mTimedLockoutCleared.put(userId, true);
-        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
-        // the alarm might still be pending; remove it.
-        cancelLockoutResetForUser(userId);
-        notifyLockoutResetMonitors();
-    }
-
-    private void cancelLockoutResetForUser(int userId) {
-        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
-    }
 
     private void listenForUserSwitches() {
         try {
@@ -1204,12 +1301,6 @@
         }
     }
 
-    private void notifyLockoutResetMonitors() {
-        for (int i = 0; i < mLockoutMonitors.size(); i++) {
-            mLockoutMonitors.get(i).sendLockoutReset();
-        }
-    }
-
     private void removeLockoutResetCallback(
             LockoutResetMonitor monitor) {
         mLockoutMonitors.remove(monitor);
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 0f57f48..44ac037 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -39,6 +39,10 @@
     }
 
     @Override
+    public void notifyUserActivity() {
+    }
+
+    @Override
     protected int statsAction() {
         return BiometricsProtoEnums.ACTION_ENUMERATE;
     }
@@ -94,7 +98,9 @@
     public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         try {
-            getListener().onEnumerated(identifier, remaining);
+            if (getListener() != null) {
+                getListener().onEnumerated(identifier, remaining);
+            }
         } catch (RemoteException e) {
             Slog.w(getLogTag(), "Failed to notify enumerated:", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 0509067..a18f336 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -44,6 +44,10 @@
     }
 
     @Override
+    public void notifyUserActivity() {
+    }
+
+    @Override
     protected int statsAction() {
         return BiometricsProtoEnums.ACTION_REMOVE;
     }
@@ -95,7 +99,9 @@
     private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         try {
-            getListener().onRemoved(identifier, remaining);
+            if (getListener() != null) {
+                getListener().onRemoved(identifier, remaining);
+            }
         } catch (RemoteException e) {
             Slog.w(getLogTag(), "Failed to notify Removed:", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 8995068..d2d1482 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -51,9 +51,13 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
+import com.android.server.biometrics.ClientMonitor;
+import com.android.server.biometrics.EnumerateClient;
 import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -79,8 +83,6 @@
     private static final String FACE_DATA_DIR = "facedata";
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
     private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
     private final class FaceAuthClient extends AuthenticationClientImpl {
@@ -96,6 +98,25 @@
         protected int statsModality() {
             return FaceService.this.statsModality();
         }
+
+        @Override
+        public boolean shouldFrameworkHandleLockout() {
+            return false;
+        }
+
+        @Override
+        public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+                boolean authenticated, ArrayList<Byte> token) {
+            final boolean result = super.onAuthenticated(identifier, authenticated, token);
+
+            // For face, the authentication lifecycle ends either when
+            // 1) Authenticated == true
+            // 2) Error occurred
+            // 3) Authenticated == false
+            // Fingerprint currently does not end when the third condition is met which is a bug,
+            // but let's leave it as-is for now.
+            return result || !authenticated;
+        }
     }
 
     /**
@@ -106,6 +127,7 @@
         /**
          * The following methods contain common code which is shared in biometrics/common.
          */
+
         @Override // Binder call
         public long generateChallenge(IBinder token) {
             checkPermission(MANAGE_BIOMETRIC);
@@ -216,15 +238,14 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
-                    userId, restricted, token.toString()) {
+            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
+                    0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
                 protected int statsModality() {
                     return FaceService.this.statsModality();
                 }
             };
-            client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
 
@@ -234,9 +255,9 @@
             checkPermission(MANAGE_BIOMETRIC);
 
             final boolean restricted = isRestricted();
-            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName()) {
+            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+                    userId, restricted, getContext().getOpPackageName()) {
                 @Override
                 protected int statsModality() {
                     return FaceService.this.statsModality();
@@ -317,7 +338,7 @@
                 return null;
             }
 
-            return FaceService.this.getEnrolledFaces(userId);
+            return FaceService.this.getEnrolledTemplates(userId);
         }
 
         @Override // Binder call
@@ -354,10 +375,13 @@
         }
 
         @Override // Binder call
-        public void resetTimeout(byte[] token) {
+        public void resetLockout(byte[] token) {
             checkPermission(MANAGE_BIOMETRIC);
-            // TODO: confirm security token when we move timeout management into the HAL layer.
-            mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+            try {
+                mDaemonWrapper.resetLockout(token);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Unable to reset lockout", e);
+            }
         }
 
         @Override
@@ -511,7 +535,8 @@
         public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFaceServiceReceiver != null) {
-
+                mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
+                        identifier.getBiometricId(), remaining);
             }
         }
     }
@@ -520,75 +545,107 @@
 
     @GuardedBy("this")
     private IBiometricsFace mDaemon;
-    private long mHalDeviceId;
+    // One of the AuthenticationClient constants
+    private int mCurrentUserLockoutMode;
 
     /**
      * Receives callbacks from the HAL.
      */
     private IBiometricsFaceClientCallback mDaemonCallback =
             new IBiometricsFaceClientCallback.Stub() {
-                @Override
-                public void onEnrollResult(final long deviceId, int faceId, int userId,
-                        int remaining) {
-                    mHandler.post(() -> {
-                        final Face face = new Face(getBiometricUtils()
-                                .getUniqueName(getContext(), userId), faceId, deviceId);
-                        FaceService.super.handleEnrollResult(face, remaining);
-                    });
-                }
+        @Override
+        public void onEnrollResult(final long deviceId, int faceId, int userId,
+                int remaining) {
+            mHandler.post(() -> {
+                final Face face = new Face(getBiometricUtils()
+                        .getUniqueName(getContext(), userId), faceId, deviceId);
+                FaceService.super.handleEnrollResult(face, remaining);
+            });
+        }
 
-                @Override
-                public void onAcquired(final long deviceId, final int userId,
-                        final int acquiredInfo,
-                        final int vendorCode) {
-                    mHandler.post(() -> {
-                        FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
-                    });
-                }
+        @Override
+        public void onAcquired(final long deviceId, final int userId,
+                final int acquiredInfo,
+                final int vendorCode) {
+            mHandler.post(() -> {
+                FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+            });
+        }
 
-                @Override
-                public void onAuthenticated(final long deviceId, final int faceId, final int userId,
-                        ArrayList<Byte> token) {
-                    mHandler.post(() -> {
-                        Face face = new Face("", faceId, deviceId);
-                        FaceService.super.handleAuthenticated(face, token);
-                    });
-                }
+        @Override
+        public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+                ArrayList<Byte> token) {
+            mHandler.post(() -> {
+                Face face = new Face("", faceId, deviceId);
+                FaceService.super.handleAuthenticated(face, token);
+            });
+        }
 
-                @Override
-                public void onError(final long deviceId, final int userId, final int error,
-                        final int vendorCode) {
-                    mHandler.post(() -> {
-                        FaceService.super.handleError(deviceId, error, vendorCode);
+        @Override
+        public void onError(final long deviceId, final int userId, final int error,
+                final int vendorCode) {
+            mHandler.post(() -> {
+                FaceService.super.handleError(deviceId, error, vendorCode);
 
-                        // TODO: this chunk of code should be common to all biometric services
-                        if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
-                            // If we get HW_UNAVAILABLE, try to connect again later...
-                            Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
-                            synchronized (this) {
-                                mDaemon = null;
-                                mHalDeviceId = 0;
-                                mCurrentUserId = UserHandle.USER_NULL;
-                            }
-                        }
-                    });
+                // TODO: this chunk of code should be common to all biometric services
+                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+                    // If we get HW_UNAVAILABLE, try to connect again later...
+                    Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+                    synchronized (this) {
+                        mDaemon = null;
+                        mHalDeviceId = 0;
+                        mCurrentUserId = UserHandle.USER_NULL;
+                    }
                 }
+            });
+        }
 
-                @Override
-                public void onRemoved(final long deviceId, final int faceId, final int userId,
-                        final int remaining) {
-                    mHandler.post(() -> {
-                        final Face face = new Face("", faceId, deviceId);
-                        FaceService.super.handleRemoved(face, remaining);
-                    });
-                }
+        @Override
+        public void onRemoved(final long deviceId, final int faceId, final int userId,
+                final int remaining) {
+            mHandler.post(() -> {
+                final Face face = new Face("", faceId, deviceId);
+                FaceService.super.handleRemoved(face, remaining);
+            });
+        }
 
-                @Override
-                public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
-                        throws RemoteException {
-                    // TODO
+        @Override
+        public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+                throws RemoteException {
+            mHandler.post(() -> {
+                if (!faceIds.isEmpty()) {
+                    for (int i = 0; i < faceIds.size(); i++) {
+                        final Face face = new Face("", faceIds.get(i), deviceId);
+                        // Convert to old old behavior
+                        FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
+                    }
+                } else {
+                    // For face, the HIDL contract is to receive an empty list when there are no
+                    // templates enrolled. Send a null identifier since we don't consume them
+                    // anywhere, and send remaining == 0 to plumb this with existing common code.
+                    FaceService.super.handleEnumerate(null /* identifier */, 0);
                 }
-            };
+            });
+        }
+
+        @Override
+        public void onLockoutChanged(long duration) {
+            Slog.d(TAG, "onLockoutChanged: " + duration);
+            if (duration == 0) {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
+            } else if (duration == Long.MAX_VALUE) {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
+            } else {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
+            }
+
+            mHandler.post(() -> {
+                if (duration == 0) {
+                    notifyLockoutResetMonitors();
+                }
+            });
+        }
+    };
 
     /**
      * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
@@ -647,9 +704,22 @@
             for (int i = 0; i < cryptoToken.length; i++) {
                 token.add(cryptoToken[i]);
             }
-            // TODO: plumb requireAttention down from framework
             return daemon.enroll(token, timeout, disabledFeatures);
         }
+
+        @Override
+        public void resetLockout(byte[] cryptoToken) throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "resetLockout(): no face HAL!");
+                return;
+            }
+            final ArrayList<Byte> token = new ArrayList<>();
+            for (int i = 0; i < cryptoToken.length; i++) {
+                token.add(cryptoToken[i]);
+            }
+            daemon.resetLockout(token);
+        }
     };
 
 
@@ -670,21 +740,16 @@
     }
 
     @Override
+    protected DaemonWrapper getDaemonWrapper() {
+        return mDaemonWrapper;
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FaceUtils.getInstance();
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
-    }
-
-    @Override
     protected Metrics getMetrics() {
         return mFaceMetrics;
     }
@@ -693,7 +758,7 @@
     protected boolean hasReachedEnrollmentLimit(int userId) {
         final int limit = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
-        final int enrolled = FaceService.this.getEnrolledFaces(userId).size();
+        final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
         if (enrolled >= limit) {
             Slog.w(TAG, "Too many faces registered");
             return true;
@@ -761,7 +826,9 @@
 
     @Override
     protected void handleUserSwitching(int userId) {
-        updateActiveGroup(userId, null);
+        super.handleUserSwitching(userId);
+        // Will be updated when we get the callback from HAL
+        mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
     }
 
     @Override
@@ -780,7 +847,6 @@
     @Override
     protected void checkUseBiometricPermission() {
         // noop for Face. The permission checks are all done on the incoming binder call.
-        // TODO: Perhaps do the same in FingerprintService
     }
 
     @Override
@@ -790,6 +856,11 @@
     }
 
     @Override
+    protected List<Face> getEnrolledTemplates(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
         // noop for Face.
     }
@@ -799,6 +870,11 @@
         return BiometricsProtoEnums.MODALITY_FACE;
     }
 
+    @Override
+    protected int getLockoutMode() {
+        return mCurrentUserLockoutMode;
+    }
+
     /** Gets the face daemon */
     private synchronized IBiometricsFace getFaceDaemon() {
         if (mDaemon == null) {
@@ -828,6 +904,7 @@
             if (mHalDeviceId != 0) {
                 loadAuthenticatorIds();
                 updateActiveGroup(ActivityManager.getCurrentUser(), null);
+                doTemplateCleanupForUser(ActivityManager.getCurrentUser());
             } else {
                 Slog.w(TAG, "Failed to open Face HAL!");
                 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
@@ -865,10 +942,6 @@
         return 0;
     }
 
-    private List<Face> getEnrolledFaces(int userId) {
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
-    }
-
     private void dumpInternal(PrintWriter pw) {
         JSONObject dump = new JSONObject();
         try {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index d8544e3..164468e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -24,8 +24,13 @@
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -46,21 +51,25 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SELinux;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.StatsLog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.ClientMonitor;
 import com.android.server.biometrics.EnumerateClient;
 import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -85,20 +94,30 @@
 
     protected static final String TAG = "FingerprintService";
     private static final boolean DEBUG = true;
-    private static final boolean CLEANUP_UNUSED_FP = true;
     private static final String FP_DATA_DIR = "fpdata";
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+    private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
 
-    // TODO: This should be refactored into BiometricService
-    private final class UserFingerprint {
-        Fingerprint f;
-        int userId;
-        public UserFingerprint(Fingerprint f, int userId) {
-            this.f = f;
-            this.userId = userId;
+    private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+        @Override
+        public void run() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+    }
+
+    private final class LockoutReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+            if (getLockoutResetIntent().equals(intent.getAction())) {
+                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+            }
         }
     }
 
@@ -121,6 +140,30 @@
         protected int statsModality() {
             return FingerprintService.this.statsModality();
         }
+
+        @Override
+        public void resetFailedAttempts() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+
+        @Override
+        public boolean shouldFrameworkHandleLockout() {
+            return true;
+        }
+
+        @Override
+        public int handleFailedAttempt() {
+            final int currentUser = ActivityManager.getCurrentUser();
+            mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+            mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+
+            if (getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+                scheduleLockoutResetForUser(currentUser);
+            }
+
+            return super.handleFailedAttempt();
+        }
     }
 
     /**
@@ -241,15 +284,14 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
-                    userId, restricted, token.toString()) {
+            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+                    fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
                 protected int statsModality() {
                     return FingerprintService.this.statsModality();
                 }
             };
-            client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
 
@@ -259,9 +301,9 @@
             checkPermission(MANAGE_FINGERPRINT);
 
             final boolean restricted = isRestricted();
-            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName()) {
+            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+                    userId, restricted, getContext().getOpPackageName()) {
                 @Override
                 protected int statsModality() {
                     return FingerprintService.this.statsModality();
@@ -339,7 +381,7 @@
                 return Collections.emptyList();
             }
 
-            return FingerprintService.this.getEnrolledFingerprints(userId);
+            return FingerprintService.this.getEnrolledTemplates(userId);
         }
 
         @Override // Binder call
@@ -445,7 +487,6 @@
         public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFingerprintServiceReceiver != null) {
-                // TODO: Pass up the fp directly instead
                 final Fingerprint fp = (Fingerprint) identifier;
                 mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(),
                         fp.getGroupId(), remaining);
@@ -493,7 +534,6 @@
         public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFingerprintServiceReceiver != null) {
-                // TODO: Pass up the fp directly instead
                 final Fingerprint fp = (Fingerprint) identifier;
                 mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(),
                         fp.getGroupId(), remaining);
@@ -511,105 +551,18 @@
         }
     }
 
-    /**
-     * An internal class to help clean up unknown fingerprints in the hardware and software.
-     */
-    private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl {
-
-        private List<Fingerprint> mEnrolledList;
-        private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
-        public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int groupId, int userId,
-                boolean restricted, String owner, List<Fingerprint> enrolledList) {
-            super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
-                    owner);
-            mEnrolledList = enrolledList;
-        }
-
-        private void handleEnumeratedFingerprint(
-                BiometricAuthenticator.Identifier identifier, int remaining) {
-            boolean matched = false;
-            for (int i = 0; i < mEnrolledList.size(); i++) {
-                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
-                    mEnrolledList.remove(i);
-                    matched = true;
-                    break;
-                }
-            }
-
-            // fingerId 0 means no fingerprints are in hardware
-            if (!matched && identifier.getBiometricId() != 0) {
-                mUnknownFingerprints.add((Fingerprint) identifier);
-            }
-        }
-
-        private void doFingerprintCleanup() {
-            if (mEnrolledList == null) {
-                return;
-            }
-
-            for (Fingerprint f : mEnrolledList) {
-                Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
-                        + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId()
-                        + " " + f.getDeviceId());
-                FingerprintUtils.getInstance().removeBiometricForUser(getContext(),
-                        getTargetUserId(), f.getBiometricId());
-                StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
-            }
-            mEnrolledList.clear();
-        }
-
-        public List<Fingerprint> getUnknownFingerprints() {
-            return mUnknownFingerprints;
-        }
-
-        @Override
-        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
-                int remaining) {
-            handleEnumeratedFingerprint(identifier, remaining);
-            if (remaining == 0) {
-                doFingerprintCleanup();
-            }
-            return remaining == 0;
-        }
-
-        @Override
-        protected int statsModality() {
-            return FingerprintService.this.statsModality();
-        }
-    }
-
-    /**
-     * An internal class to help clean up unknown fingerprints in hardware and software.
-     */
-    private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl {
-        public InternalRemovalClient(Context context,
-                DaemonWrapper daemon, long halDeviceId, IBinder token,
-                ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
-                String owner) {
-            super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
-                    restricted,
-                    owner);
-        }
-
-        @Override
-        protected int statsModality() {
-            return FingerprintService.this.statsModality();
-        }
-    }
-
     private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
     private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
             new CopyOnWriteArrayList<>();
 
     @GuardedBy("this")
     private IBiometricsFingerprint mDaemon;
-
-    private long mHalDeviceId;
-    private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
-    private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+    private final SparseBooleanArray mTimedLockoutCleared;
+    private final SparseIntArray mFailedAttempts;
+    private final AlarmManager mAlarmManager;
+    private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+    protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+            new ResetFailedAttemptsForUserRunnable();
 
     /**
      * Receives callbacks from the HAL.
@@ -646,13 +599,7 @@
         @Override
         public void onError(final long deviceId, final int error, final int vendorCode) {
             mHandler.post(() -> {
-                ClientMonitor client = getCurrentClient();
-                if (client instanceof InternalRemovalClient
-                        || client instanceof InternalEnumerateClient) {
-                    clearEnumerateState();
-                }
                 FingerprintService.super.handleError(deviceId, error, vendorCode);
-
                 // TODO: this chunk of code should be common to all biometric services
                 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
                     // If we get HW_UNAVAILABLE, try to connect again later...
@@ -673,11 +620,6 @@
                 ClientMonitor client = getCurrentClient();
                 final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                 FingerprintService.super.handleRemoved(fp, remaining);
-                if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
-                    cleanupUnknownFingerprints();
-                } else if (client instanceof InternalRemovalClient){
-                    clearEnumerateState();
-                }
             });
         }
 
@@ -685,9 +627,8 @@
         public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
                 final int remaining) {
             mHandler.post(() -> {
-                // TODO: factor out common enumerate logic if possible
                 final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
-                FingerprintService.this.handleEnumerate(fp, remaining);
+                FingerprintService.super.handleEnumerate(fp, remaining);
             });
 
         }
@@ -748,10 +689,22 @@
             }
             return daemon.enroll(cryptoToken, groupId, timeout);
         }
+
+        @Override
+        public void resetLockout(byte[] token) throws RemoteException {
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            Slog.e(TAG, "Not supported");
+            return;
+        }
     };
 
     public FingerprintService(Context context) {
         super(context);
+        mTimedLockoutCleared = new SparseBooleanArray();
+        mFailedAttempts = new SparseIntArray();
+        mAlarmManager = context.getSystemService(AlarmManager.class);
+        context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+                getLockoutBroadcastPermission(), null /* handler */);
     }
 
     @Override
@@ -767,21 +720,16 @@
     }
 
     @Override
+    protected DaemonWrapper getDaemonWrapper() {
+        return mDaemonWrapper;
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FingerprintUtils.getInstance();
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
-    }
-
-    @Override
     protected Metrics getMetrics() {
         return mFingerprintMetrics;
     }
@@ -790,7 +738,7 @@
     protected boolean hasReachedEnrollmentLimit(int userId) {
         final int limit = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-        final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+        final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
         if (enrolled >= limit) {
             Slog.w(TAG, "Too many fingerprints registered");
             return true;
@@ -866,19 +814,6 @@
     }
 
     @Override
-    protected void handleUserSwitching(int userId) {
-        if (getCurrentClient() instanceof InternalRemovalClient
-                || getCurrentClient() instanceof InternalEnumerateClient) {
-            Slog.w(TAG, "User switched while performing cleanup");
-            removeClient(getCurrentClient());
-            clearEnumerateState();
-        }
-        updateActiveGroup(userId, null);
-        doFingerprintCleanupForUser(userId);
-    }
-
-
-    @Override
     protected boolean hasEnrolledBiometrics(int userId) {
         if (userId != UserHandle.getCallingUserId()) {
             checkPermission(INTERACT_ACROSS_USERS);
@@ -913,6 +848,11 @@
     }
 
     @Override
+    protected List<Fingerprint> getEnrolledTemplates(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
         List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
         for (int i = 0; i < callbacks.size(); i++) {
@@ -930,6 +870,20 @@
         return BiometricsProtoEnums.MODALITY_FINGERPRINT;
     }
 
+    @Override
+    protected int getLockoutMode() {
+        final int currentUser = ActivityManager.getCurrentUser();
+        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+        if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+            return AuthenticationClient.LOCKOUT_PERMANENT;
+        } else if (failedAttempts > 0
+                && !mTimedLockoutCleared.get(currentUser, false)
+                && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+            return AuthenticationClient.LOCKOUT_TIMED;
+        }
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
+
     /** Gets the fingerprint daemon */
     private synchronized IBiometricsFingerprint getFingerprintDaemon() {
         if (mDaemon == null) {
@@ -959,7 +913,7 @@
             if (mHalDeviceId != 0) {
                 loadAuthenticatorIds();
                 updateActiveGroup(ActivityManager.getCurrentUser(), null);
-                doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+                doTemplateCleanupForUser(ActivityManager.getCurrentUser());
             } else {
                 Slog.w(TAG, "Failed to open Fingerprint HAL!");
                 MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
@@ -969,79 +923,6 @@
         return mDaemon;
     }
 
-    /**
-     * This method should be called upon connection to the daemon, and when user switches.
-     * @param userId
-     */
-    private void doFingerprintCleanupForUser(int userId) {
-        if (CLEANUP_UNUSED_FP) {
-            enumerateUser(userId);
-        }
-    }
-
-    private void clearEnumerateState() {
-        if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
-        mUnknownFingerprints.clear();
-    }
-
-    private void enumerateUser(int userId) {
-        if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
-
-        final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-        final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
-
-        InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
-                mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
-                getContext().getOpPackageName(), enrolledList);
-        enumerateInternal(client);
-    }
-
-    // Remove unknown fingerprints from hardware
-    private void cleanupUnknownFingerprints() {
-        if (!mUnknownFingerprints.isEmpty()) {
-            UserFingerprint uf = mUnknownFingerprints.get(0);
-            mUnknownFingerprints.remove(uf);
-            boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-            InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
-                    mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(),
-                    uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
-            removeInternal(client);
-            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
-                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
-        } else {
-            clearEnumerateState();
-        }
-    }
-
-    private void handleEnumerate(Fingerprint fingerprint, int remaining) {
-        ClientMonitor client = getCurrentClient();
-
-        if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
-            return;
-        }
-        client.onEnumerationResult(fingerprint, remaining);
-
-        // All fingerprints in hardware for this user were enumerated
-        if (remaining == 0) {
-            if (client instanceof InternalEnumerateClient) {
-                List<Fingerprint> unknownFingerprints =
-                        ((InternalEnumerateClient) client).getUnknownFingerprints();
-
-                if (!unknownFingerprints.isEmpty()) {
-                    Slog.w(TAG, "Adding " + unknownFingerprints.size() +
-                            " fingerprints for deletion");
-                }
-                for (Fingerprint f : unknownFingerprints) {
-                    mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
-                }
-                removeClient(client);
-                cleanupUnknownFingerprints();
-            } else {
-                removeClient(client);
-            }
-        }
-    }
-
     private long startPreEnroll(IBinder token) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
@@ -1070,8 +951,38 @@
         return 0;
     }
 
-    private List<Fingerprint> getEnrolledFingerprints(int userId) {
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    // Attempt counter should only be cleared when Keyguard goes away or when
+    // a biometric is successfully authenticated. Lockout should eventually be done below the HAL.
+    // See AuthenticationClient#shouldFrameworkHandleLockout().
+    private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+        }
+        if (clearAttemptCounter) {
+            mFailedAttempts.put(userId, 0);
+        }
+        mTimedLockoutCleared.put(userId, true);
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutResetForUser(userId);
+        notifyLockoutResetMonitors();
+    }
+
+
+    private void cancelLockoutResetForUser(int userId) {
+        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+    }
+
+    private void scheduleLockoutResetForUser(int userId) {
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+                getLockoutResetIntentForUser(userId));
+    }
+
+    private PendingIntent getLockoutResetIntentForUser(int userId) {
+        return PendingIntent.getBroadcast(getContext(), userId,
+                new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+                PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     private void dumpInternal(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index eb457b6..cb8a772 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -17,12 +17,16 @@
 package com.android.server.biometrics.iris;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.Metrics;
 
+import java.util.List;
+
 /**
  * A service to manage multiple clients that want to access the Iris HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
@@ -61,18 +65,13 @@
     }
 
     @Override
-    protected BiometricUtils getBiometricUtils() {
+    protected DaemonWrapper getDaemonWrapper() {
         return null;
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return 0;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return 0;
+    protected BiometricUtils getBiometricUtils() {
+        return null;
     }
 
     @Override
@@ -106,11 +105,6 @@
     }
 
     @Override
-    protected void handleUserSwitching(int userId) {
-
-    }
-
-    @Override
     protected boolean hasEnrolledBiometrics(int userId) {
         return false;
     }
@@ -131,7 +125,17 @@
     }
 
     @Override
+    protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
+        return null;
+    }
+
+    @Override
     protected int statsModality() {
         return BiometricsProtoEnums.MODALITY_IRIS;
     }
+
+    @Override
+    protected int getLockoutMode() {
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 19bdc09..c91e1a1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1859,7 +1859,7 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mDeps.getIpServerDependencies(mContext)));
+                             mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e1af81b..59fffb4 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -241,7 +241,7 @@
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-        updateCapabilities();
+        updateCapabilities(null /* defaultNetwork */);
 
         loadAlwaysOnPackage();
     }
@@ -268,22 +268,44 @@
         updateAlwaysOnNotification(detailedState);
     }
 
-    public void updateCapabilities() {
-        final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
-        // Only apps targeting Q and above can explicitly declare themselves as metered.
-        final boolean isAlwaysMetered =
-                mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered);
-        updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
-                mNetworkCapabilities, isAlwaysMetered);
-
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    /**
+     * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
+     * defensive copy.
+     *
+     * <p>Does not propagate updated capabilities to apps.
+     *
+     * @param defaultNetwork underlying network for VPNs following platform's default
+     */
+    public synchronized NetworkCapabilities updateCapabilities(
+            @Nullable Network defaultNetwork) {
+        if (mConfig == null) {
+            // VPN is not running.
+            return null;
         }
+
+        Network[] underlyingNetworks = mConfig.underlyingNetworks;
+        if (underlyingNetworks == null && defaultNetwork != null) {
+            // null underlying networks means to track the default.
+            underlyingNetworks = new Network[] { defaultNetwork };
+        }
+        // Only apps targeting Q and above can explicitly declare themselves as metered.
+        final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
+
+        applyUnderlyingCapabilities(
+                mContext.getSystemService(ConnectivityManager.class),
+                underlyingNetworks,
+                mNetworkCapabilities,
+                isAlwaysMetered);
+
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     @VisibleForTesting
-    public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
-            NetworkCapabilities caps, boolean isAlwaysMetered) {
+    public static void applyUnderlyingCapabilities(
+            ConnectivityManager cm,
+            Network[] underlyingNetworks,
+            NetworkCapabilities caps,
+            boolean isAlwaysMetered) {
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -296,6 +318,7 @@
         boolean hadUnderlyingNetworks = false;
         if (null != underlyingNetworks) {
             for (Network underlying : underlyingNetworks) {
+                // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
                 final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
                 if (underlyingCaps == null) continue;
                 hadUnderlyingNetworks = true;
@@ -895,6 +918,7 @@
         TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
         TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
         for (final RouteInfo route : routes) {
+            if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue;
             IpPrefix destination = route.getDestination();
             if (destination.isIPv4()) {
                 ipv4Prefixes.add(destination);
@@ -1007,9 +1031,8 @@
     }
 
     /**
-     * Establish a VPN network and return the file descriptor of the VPN
-     * interface. This methods returns {@code null} if the application is
-     * revoked or not prepared.
+     * Establish a VPN network and return the file descriptor of the VPN interface. This methods
+     * returns {@code null} if the application is revoked or not prepared.
      *
      * @param config The parameters to configure the network.
      * @return The file descriptor of the VPN interface.
@@ -1101,8 +1124,6 @@
                 // as rules are deleted. This prevents data leakage as the rules are moved over.
                 agentDisconnect(oldNetworkAgent);
             }
-            // Set up VPN's capabilities such as meteredness.
-            updateCapabilities();
 
             if (oldConnection != null) {
                 mContext.unbindService(oldConnection);
@@ -1258,6 +1279,11 @@
         return ranges;
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its internal capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserAdded(int userHandle) {
         // If the user is restricted tie them to the parent user's VPN
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1268,8 +1294,9 @@
                     try {
                         addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
+                        // ConnectivityService will call {@link #updateCapabilities} and apply
+                        // those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
@@ -1279,6 +1306,11 @@
         }
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserRemoved(int userHandle) {
         // clean up if restricted
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1290,8 +1322,9 @@
                         final List<UidRange> removedRanges =
                             uidRangesForUser(userHandle, existingRanges);
                         existingRanges.removeAll(removedRanges);
+                        // ConnectivityService will call {@link #updateCapabilities} and
+                        // apply those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
@@ -1504,6 +1537,12 @@
         return success;
     }
 
+    /**
+     * Updates underlying network set.
+     *
+     * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
+     * ConnectivityService thread to get updated capabilities.
+     */
     public synchronized boolean setUnderlyingNetworks(Network[] networks) {
         if (!isCallerEstablishedOwnerLocked()) {
             return false;
@@ -1520,7 +1559,6 @@
                 }
             }
         }
-        updateCapabilities();
         return true;
     }
 
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 3fddac1..173d786 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -61,8 +61,8 @@
     /**
      * Get dependencies to be used by IpServer.
      */
-    public IpServer.Dependencies getIpServerDependencies(Context context) {
-        return new IpServer.Dependencies(context);
+    public IpServer.Dependencies getIpServerDependencies() {
+        return new IpServer.Dependencies();
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 45f169c..7dd3b36 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -129,240 +129,9 @@
     private final NightDisplayTintController mNightDisplayTintController =
             new NightDisplayTintController();
 
-    private final TintController mDisplayWhiteBalanceTintController = new TintController() {
-        // Three chromaticity coordinates per color: X, Y, and Z
-        private final int NUM_VALUES_PER_PRIMARY = 3;
-        // Four colors: red, green, blue, and white
-        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
-
-        private final Object mLock = new Object();
-        private int mTemperatureMin;
-        private int mTemperatureMax;
-        private int mTemperatureDefault;
-        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-        private ColorSpace.Rgb mDisplayColorSpaceRGB;
-        private float[] mChromaticAdaptationMatrix;
-        private int mCurrentColorTemperature;
-        private float[] mCurrentColorTemperatureXYZ;
-        private boolean mSetUp = false;
-        private float[] mMatrixDisplayWhiteBalance = new float[16];
-        private Boolean mIsAvailable;
-
-        @Override
-        public void setUp(Context context, boolean needsLinear) {
-            mSetUp = false;
-            final Resources res = context.getResources();
-
-            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
-            if (displayColorSpaceRGB == null) {
-                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
-                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
-                if (displayColorSpaceRGB == null) {
-                    Slog.e(TAG, "Failed to get display color space from resources");
-                    return;
-                }
-            }
-
-            final String[] nominalWhiteValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
-            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-            for (int i = 0; i < nominalWhiteValues.length; i++) {
-                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
-            }
-
-            final int colorTemperatureMin = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
-            if (colorTemperatureMin <= 0) {
-                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
-                return;
-            }
-
-            final int colorTemperatureMax = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
-            if (colorTemperatureMax < colorTemperatureMin) {
-                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
-                return;
-            }
-
-            final int colorTemperature = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
-
-            synchronized (mLock) {
-                mDisplayColorSpaceRGB = displayColorSpaceRGB;
-                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
-                mTemperatureMin = colorTemperatureMin;
-                mTemperatureMax = colorTemperatureMax;
-                mTemperatureDefault = colorTemperature;
-                mSetUp = true;
-            }
-
-            setMatrix(mTemperatureDefault);
-        }
-
-        @Override
-        public float[] getMatrix() {
-            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
-        }
-
-        @Override
-        public void setMatrix(int cct) {
-            if (!mSetUp) {
-                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
-                return;
-            }
-
-            if (cct < mTemperatureMin) {
-                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
-                cct = mTemperatureMin;
-            } else if (cct > mTemperatureMax) {
-                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
-                cct = mTemperatureMax;
-            }
-
-            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
-
-            synchronized (mLock) {
-                mCurrentColorTemperature = cct;
-
-                // Adapt the display's nominal white point to match the requested CCT value
-                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
-
-                mChromaticAdaptationMatrix =
-                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
-                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
-
-                // Convert the adaptation matrix to RGB space
-                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
-                        mDisplayColorSpaceRGB.getTransform());
-                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
-
-                // Normalize the transform matrix to peak white value in RGB space
-                final float adaptedMaxR = result[0] + result[3] + result[6];
-                final float adaptedMaxG = result[1] + result[4] + result[7];
-                final float adaptedMaxB = result[2] + result[5] + result[8];
-                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
-                for (int i = 0; i < result.length; i++) {
-                    result[i] /= denum;
-                }
-
-                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
-                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
-                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
-                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
-            }
-        }
-
-        @Override
-        public int getLevel() {
-            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
-        }
-
-        @Override
-        public boolean isAvailable(Context context) {
-            if (mIsAvailable == null) {
-                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
-            }
-            return mIsAvailable;
-        }
-
-        /**
-         * Format a given matrix into a string.
-         *
-         * @param matrix the matrix to format
-         * @param cols number of columns in the matrix
-         */
-        private String matrixToString(float[] matrix, int cols) {
-            if (matrix == null || cols <= 0) {
-                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
-                return "";
-            }
-
-            StringBuilder sb = new StringBuilder("");
-            for (int i = 0; i < matrix.length; i++) {
-                if (i % cols == 0) {
-                    sb.append("\n    ");
-                }
-                sb.append(String.format("%9.6f ", matrix[i]));
-            }
-            return sb.toString();
-        }
-
-        @Override
-        public void dump(PrintWriter pw) {
-            synchronized (mLock) {
-                pw.println("  mSetUp = " + mSetUp);
-                if (!mSetUp) {
-                    return;
-                }
-
-                pw.println("  isActivated = " + isActivated());
-                pw.println("  mTemperatureMin = " + mTemperatureMin);
-                pw.println("  mTemperatureMax = " + mTemperatureMax);
-                pw.println("  mTemperatureDefault = " + mTemperatureDefault);
-                pw.println("  mCurrentColorTemperature = " + mCurrentColorTemperature);
-                pw.println("  mCurrentColorTemperatureXYZ = " +
-                        matrixToString(mCurrentColorTemperatureXYZ, 3));
-                pw.println("  mDisplayColorSpaceRGB RGB-to-XYZ = " +
-                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
-                pw.println("  mChromaticAdaptationMatrix = " +
-                        matrixToString(mChromaticAdaptationMatrix, 3));
-                pw.println("  mDisplayColorSpaceRGB XYZ-to-RGB = " +
-                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
-                pw.println("  mMatrixDisplayWhiteBalance = " +
-                        matrixToString(mMatrixDisplayWhiteBalance, 4));
-            }
-        }
-
-        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
-            return new ColorSpace.Rgb(
-                "Display Color Space",
-                redGreenBlueXYZ,
-                whiteXYZ,
-                2.2f // gamma, unused for display white balance
-            );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
-            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-            if (displayToken == null) {
-                return null;
-            }
-
-            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
-            if (primaries == null || primaries.red == null || primaries.green == null ||
-                primaries.blue == null || primaries.white == null) {
-                return null;
-            }
-
-            return makeRgbColorSpaceFromXYZ(
-                    new float[] {
-                        primaries.red.X, primaries.red.Y, primaries.red.Z,
-                        primaries.green.X, primaries.green.Y, primaries.green.Z,
-                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
-                    },
-                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
-                    );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
-            final String[] displayPrimariesValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayPrimaries);
-            float[] displayRedGreenBlueXYZ =
-                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
-            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-
-            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
-                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
-            }
-
-            for (int i = 0; i < displayWhiteXYZ.length; i++) {
-                displayWhiteXYZ[i] = Float.parseFloat(
-                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
-            }
-
-            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
-        }
-    };
+    @VisibleForTesting
+    final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
+            new DisplayWhiteBalanceTintController();
 
     private final TintController mGlobalSaturationTintController = new TintController() {
 
@@ -860,7 +629,8 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private void updateDisplayWhiteBalanceStatus() {
+    @VisibleForTesting
+    void updateDisplayWhiteBalanceStatus() {
         boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
         mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
                 !mNightDisplayTintController.isActivated() &&
@@ -1101,6 +871,7 @@
         pw.println("Display white balance:");
         if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
             pw.println("    Activated: " + mDisplayWhiteBalanceTintController.isActivated());
+            mDisplayWhiteBalanceTintController.dump(pw);
         } else {
             pw.println("    Not available");
         }
@@ -1533,6 +1304,244 @@
         }
     }
 
+    final class DisplayWhiteBalanceTintController extends TintController {
+        // Three chromaticity coordinates per color: X, Y, and Z
+        private final int NUM_VALUES_PER_PRIMARY = 3;
+        // Four colors: red, green, blue, and white
+        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
+
+        private final Object mLock = new Object();
+        @VisibleForTesting
+        int mTemperatureMin;
+        @VisibleForTesting
+        int mTemperatureMax;
+        private int mTemperatureDefault;
+        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+        @VisibleForTesting
+        ColorSpace.Rgb mDisplayColorSpaceRGB;
+        private float[] mChromaticAdaptationMatrix;
+        @VisibleForTesting
+        int mCurrentColorTemperature;
+        private float[] mCurrentColorTemperatureXYZ;
+        private boolean mSetUp = false;
+        private float[] mMatrixDisplayWhiteBalance = new float[16];
+        private Boolean mIsAvailable;
+
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
+            mSetUp = false;
+            final Resources res = context.getResources();
+
+            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
+            if (displayColorSpaceRGB == null) {
+                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
+                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
+                if (displayColorSpaceRGB == null) {
+                    Slog.e(TAG, "Failed to get display color space from resources");
+                    return;
+                }
+            }
+
+            final String[] nominalWhiteValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
+            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < nominalWhiteValues.length; i++) {
+                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
+            }
+
+            final int colorTemperatureMin = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
+            if (colorTemperatureMin <= 0) {
+                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
+                return;
+            }
+
+            final int colorTemperatureMax = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
+            if (colorTemperatureMax < colorTemperatureMin) {
+                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
+                return;
+            }
+
+            final int colorTemperature = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+
+            synchronized (mLock) {
+                mDisplayColorSpaceRGB = displayColorSpaceRGB;
+                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
+                mTemperatureMin = colorTemperatureMin;
+                mTemperatureMax = colorTemperatureMax;
+                mTemperatureDefault = colorTemperature;
+                mSetUp = true;
+            }
+
+            setMatrix(mTemperatureDefault);
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+            if (!mSetUp) {
+                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
+                return;
+            }
+
+            if (cct < mTemperatureMin) {
+                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
+                cct = mTemperatureMin;
+            } else if (cct > mTemperatureMax) {
+                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
+                cct = mTemperatureMax;
+            }
+
+            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
+
+            synchronized (mLock) {
+                mCurrentColorTemperature = cct;
+
+                // Adapt the display's nominal white point to match the requested CCT value
+                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+
+                mChromaticAdaptationMatrix =
+                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+
+                // Convert the adaptation matrix to RGB space
+                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
+                        mDisplayColorSpaceRGB.getTransform());
+                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
+
+                // Normalize the transform matrix to peak white value in RGB space
+                final float adaptedMaxR = result[0] + result[3] + result[6];
+                final float adaptedMaxG = result[1] + result[4] + result[7];
+                final float adaptedMaxB = result[2] + result[5] + result[8];
+                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
+                for (int i = 0; i < result.length; i++) {
+                    result[i] /= denum;
+                }
+
+                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
+                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
+                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
+                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
+            }
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+        }
+
+        @Override
+        public boolean isAvailable(Context context) {
+            if (mIsAvailable == null) {
+                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
+            }
+            return mIsAvailable;
+        }
+
+        /**
+         * Format a given matrix into a string.
+         *
+         * @param matrix the matrix to format
+         * @param cols number of columns in the matrix
+         */
+        private String matrixToString(float[] matrix, int cols) {
+            if (matrix == null || cols <= 0) {
+                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
+                return "";
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            for (int i = 0; i < matrix.length; i++) {
+                if (i % cols == 0) {
+                    sb.append("\n      ");
+                }
+                sb.append(String.format("%9.6f ", matrix[i]));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void dump(PrintWriter pw) {
+            synchronized (mLock) {
+                pw.println("    mSetUp = " + mSetUp);
+                if (!mSetUp) {
+                    return;
+                }
+
+                pw.println("    mTemperatureMin = " + mTemperatureMin);
+                pw.println("    mTemperatureMax = " + mTemperatureMax);
+                pw.println("    mTemperatureDefault = " + mTemperatureDefault);
+                pw.println("    mCurrentColorTemperature = " + mCurrentColorTemperature);
+                pw.println("    mCurrentColorTemperatureXYZ = " +
+                        matrixToString(mCurrentColorTemperatureXYZ, 3));
+                pw.println("    mDisplayColorSpaceRGB RGB-to-XYZ = " +
+                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
+                pw.println("    mChromaticAdaptationMatrix = " +
+                        matrixToString(mChromaticAdaptationMatrix, 3));
+                pw.println("    mDisplayColorSpaceRGB XYZ-to-RGB = " +
+                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
+                pw.println("    mMatrixDisplayWhiteBalance = " +
+                        matrixToString(mMatrixDisplayWhiteBalance, 4));
+            }
+        }
+
+        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
+            return new ColorSpace.Rgb(
+                "Display Color Space",
+                redGreenBlueXYZ,
+                whiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
+            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+            if (displayToken == null) {
+                return null;
+            }
+
+            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+            if (primaries == null || primaries.red == null || primaries.green == null ||
+                primaries.blue == null || primaries.white == null) {
+                return null;
+            }
+
+            return makeRgbColorSpaceFromXYZ(
+                    new float[] {
+                        primaries.red.X, primaries.red.Y, primaries.red.Z,
+                        primaries.green.X, primaries.green.Y, primaries.green.Z,
+                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
+                    },
+                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
+                    );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            float[] displayRedGreenBlueXYZ =
+                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
+        }
+    };
+
     /**
      * Local service that allows color transforms to be enabled from other system services.
      */
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 6ee5665..e9ae516 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
+import android.view.DisplayAddress;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -225,8 +226,12 @@
         viewport.deviceHeight = isRotated ? info.width : info.height;
 
         viewport.uniqueId = info.uniqueId;
-        // TODO(b/112898898) Use an actual port here.
-        viewport.physicalPort = null;
+
+        if (info.address instanceof DisplayAddress.Physical) {
+            viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort();
+        } else {
+            viewport.physicalPort = null;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ab64f61..729ea17 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -19,6 +19,7 @@
 import android.hardware.display.DisplayViewport;
 import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayCutout;
 import android.view.Surface;
 
@@ -274,7 +275,7 @@
      * Display address, or null if none.
      * Interpretation varies by display type.
      */
-    public String address;
+    public DisplayAddress address;
 
     /**
      * Display state.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 80ea1da..b99ba7e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1336,7 +1336,7 @@
                 && !TextUtils.isEmpty(info.uniqueId)) {
             viewportType = VIEWPORT_VIRTUAL;
         } else {
-            Slog.wtf(TAG, "Unable to populate viewport for display device: " + info);
+            Slog.i(TAG, "Display " + info + " does not support input device matching.");
             return;
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dc5be6a..15c7ef7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1019,7 +1019,7 @@
         if (mDisplayWhiteBalanceController != null) {
             if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
                 mDisplayWhiteBalanceController.setEnabled(true);
-                mDisplayWhiteBalanceController.updateScreenColorTemperature();
+                mDisplayWhiteBalanceController.updateDisplayColorTemperature();
             } else {
                 mDisplayWhiteBalanceController.setEnabled(false);
             }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 28f21f63..4891947 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,6 +31,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
@@ -382,6 +383,7 @@
                 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
                 mInfo.state = mState;
                 mInfo.uniqueId = getUniqueId();
+                mInfo.address = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index e65637f..2f507d1 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -308,7 +308,7 @@
                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
                 }
                 mInfo.type = Display.TYPE_OVERLAY;
-                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+                mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
                 mInfo.state = mState;
             }
             return mInfo;
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index e8d6ad4..9e4c1cb 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -16,9 +16,6 @@
 
 package com.android.server.display;
 
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,9 +32,13 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -581,7 +582,7 @@
         private final int mHeight;
         private final float mRefreshRate;
         private final int mFlags;
-        private final String mAddress;
+        private final DisplayAddress mAddress;
         private final Display.Mode mMode;
 
         private Surface mSurface;
@@ -596,7 +597,7 @@
             mHeight = height;
             mRefreshRate = refreshRate;
             mFlags = flags;
-            mAddress = address;
+            mAddress = DisplayAddress.fromMacAddress(address);
             mSurface = surface;
             mMode = createMode(width, height, refreshRate);
         }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index b9aa34e8..d95e92b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -29,14 +29,14 @@
 
 /**
  * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
- * screen color temperature depending on the ambient color temperature).
+ * display color temperature depending on the ambient color temperature).
  *
  * The DisplayWhiteBalanceController:
  * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
  * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
  *   noise, and arrive at an estimate of the actual ambient color temperature;
- * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
- *   updated, suppressing changes that are too frequent or too minor.
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should
+ *   be updated, suppressing changes that are too frequent or too minor.
  */
 public class DisplayWhiteBalanceController implements
         AmbientSensor.AmbientBrightnessSensor.Callbacks,
@@ -76,8 +76,8 @@
     // Override the ambient color temperature for debugging purposes.
     private float mAmbientColorTemperatureOverride;
 
-    // A piecewise linear relationship between ambient and display color temperatures
-    private Spline.LinearSpline mAmbientToDisplayTemperatureSpline;
+    // A piecewise linear relationship between ambient and display color temperatures.
+    private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
 
     /**
      * @param brightnessSensor
@@ -91,7 +91,7 @@
      *      The filter used to average ambient color temperature changes over time, filter out the
      *      noise and arrive at an estimate of the actual ambient color temperature.
      * @param throttler
-     *      The throttler used to determine whether the new screen color temperature should be
+     *      The throttler used to determine whether the new display color temperature should be
      *      updated or not.
      * @param lowLightAmbientBrightnessThreshold
      *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
@@ -99,6 +99,12 @@
      * @param lowLightAmbientColorTemperature
      *      The ambient color temperature to which we fall back when the ambient brightness drops
      *      beneath a certain threshold.
+     * @param ambientColorTemperatures
+     *      The ambient color tempeartures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
+     * @param displayColorTemperatures
+     *      The display color temperatures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
      *
      * @throws NullPointerException
      *      - brightnessSensor is null;
@@ -114,7 +120,7 @@
             @NonNull AmbientFilter colorTemperatureFilter,
             @NonNull DisplayWhiteBalanceThrottler throttler,
             float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature,
-            float[] ambientTemperatures, float[] displayTemperatures) {
+            float[] ambientColorTemperatures, float[] displayColorTemperatures) {
         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
                 colorTemperatureFilter, throttler);
         mLoggingEnabled = false;
@@ -134,10 +140,10 @@
         mAmbientColorTemperatureOverride = -1.0f;
 
         try {
-            mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures,
-                    displayTemperatures);
+            mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+                    ambientColorTemperatures, displayColorTemperatures);
         } catch (Exception e) {
-            mAmbientToDisplayTemperatureSpline = null;
+            mAmbientToDisplayColorTemperatureSpline = null;
         }
 
         mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
@@ -160,7 +166,7 @@
     }
 
     /**
-     * Set an object to call back to when the screen color temperature should be updated.
+     * Set an object to call back to when the display color temperature should be updated.
      *
      * @param callbacks
      *      The object to call back to.
@@ -201,7 +207,7 @@
      *
      * This is only applied when the ambient color temperature changes or is updated (in which case
      * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
-     * change the screen color temperature immediately.
+     * change the display color temperature immediately.
      *
      * @param ambientColorTemperatureOverride
      *      The ambient color temperature override.
@@ -240,9 +246,8 @@
         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
-        writer.println("  mAmbientToDisplayTemperatureSpline="
-                + (mAmbientToDisplayTemperatureSpline == null ? "unused" :
-                    mAmbientToDisplayTemperatureSpline));
+        writer.println("  mAmbientToDisplayColorTemperatureSpline="
+                + mAmbientToDisplayColorTemperatureSpline);
     }
 
     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -266,9 +271,9 @@
         final long time = System.currentTimeMillis();
         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
 
-        if (mAmbientToDisplayTemperatureSpline != null) {
+        if (mAmbientToDisplayColorTemperatureSpline != null) {
             ambientColorTemperature =
-                mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature);
+                mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
         }
 
         final float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -290,7 +295,7 @@
             ambientColorTemperature = mAmbientColorTemperatureOverride;
         }
 
-        // When the screen color temperature needs to be updated, we call DisplayPowerController to
+        // When the display color temperature needs to be updated, we call DisplayPowerController to
         // call our updateColorTemperature. The reason we don't call it directly is that we want
         // all changes to the system to happen in a predictable order in DPC's main loop
         // (updatePowerState).
@@ -308,9 +313,9 @@
     }
 
     /**
-     * Updates the screen color temperature.
+     * Updates the display color temperature.
      */
-    public void updateScreenColorTemperature() {
+    public void updateDisplayColorTemperature() {
         float ambientColorTemperature = -1.0f;
 
         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
@@ -353,7 +358,7 @@
          * Called whenever the display white-balance state has changed.
          *
          * Usually, this means the estimated ambient color temperature has changed enough, and the
-         * screen color temperature should be updated; but it is also called by
+         * display color temperature should be updated; but it is also called if settings change.
          */
         void updateWhiteBalance();
     }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 56f4ca3..449f115 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -67,14 +67,14 @@
         final float lowLightAmbientColorTemperature = getFloat(resources,
                 com.android.internal.R.dimen
                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
-        final float[] ambientTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues);
-        final float[] displayTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues);
+        final float[] ambientColorTemperatures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
+        final float[] displayColorTempeartures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
         final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
                 throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature,
-                ambientTemperatures, displayTemperatures);
+                ambientColorTemperatures, displayColorTempeartures);
         brightnessSensor.setCallbacks(controller);
         colorTemperatureSensor.setCallbacks(controller);
         return controller;
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
index c1f0e98..5941bbb 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -23,7 +23,7 @@
 
 /**
  * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
- * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * display color temperature should be updated, suppressing changes that are too frequent or too
  * minor.
  */
 class DisplayWhiteBalanceThrottler {
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index a68ceed..6899c3f 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -22,24 +22,33 @@
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.gamedriver.GameDriverProto.Blacklist;
+import android.gamedriver.GameDriverProto.Blacklists;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.Base64;
 import android.util.Slog;
 
+import com.android.framework.protobuf.InvalidProtocolBufferException;
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Service to manage GPU related features.
@@ -52,17 +61,25 @@
     public static final boolean DEBUG = false;
 
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
-    private static final String WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+    private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
 
     private final Context mContext;
     private final String mDriverPackageName;
     private final PackageManager mPackageManager;
+    private final Object mLock = new Object();
+    private ContentResolver mContentResolver;
+    private long mGameDriverVersionCode;
+    private SettingsObserver mSettingsObserver;
+    @GuardedBy("mLock")
+    private Blacklists mBlacklists;
 
     public GpuService(Context context) {
         super(context);
 
         mContext = context;
         mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+        mGameDriverVersionCode = -1;
         mPackageManager = context.getPackageManager();
         if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) {
             final IntentFilter packageFilter = new IntentFilter();
@@ -82,10 +99,37 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_BOOT_COMPLETED) {
+            mContentResolver = mContext.getContentResolver();
+            mSettingsObserver = new SettingsObserver();
             if (mDriverPackageName == null || mDriverPackageName.isEmpty()) {
                 return;
             }
             fetchGameDriverPackageProperties();
+            processBlacklists();
+            setBlacklist();
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        private final Uri mGameDriverBlackUri =
+                Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
+
+        SettingsObserver() {
+            super(new Handler());
+            mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (uri == null) {
+                return;
+            }
+
+            if (mGameDriverBlackUri.equals(uri)) {
+                processBlacklists();
+                setBlacklist();
+            }
         }
     }
 
@@ -109,6 +153,7 @@
                 case ACTION_PACKAGE_CHANGED:
                 case ACTION_PACKAGE_REMOVED:
                     fetchGameDriverPackageProperties();
+                    setBlacklist();
                     break;
                 default:
                     // do nothing
@@ -138,16 +183,22 @@
             return;
         }
 
+        // Reset the whitelist.
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_WHITELIST, "");
+        mGameDriverVersionCode = driverInfo.longVersionCode;
+
         try {
             final Context driverContext = mContext.createPackageContext(mDriverPackageName,
                                                                         Context.CONTEXT_RESTRICTED);
             final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME)));
+                    new InputStreamReader(driverContext.getAssets()
+                                              .open(GAME_DRIVER_WHITELIST_FILENAME)));
             final ArrayList<String> whitelistedPackageNames = new ArrayList<>();
             for (String packageName; (packageName = reader.readLine()) != null; ) {
                 whitelistedPackageNames.add(packageName);
             }
-            Settings.Global.putString(mContext.getContentResolver(),
+            Settings.Global.putString(mContentResolver,
                                       Settings.Global.GAME_DRIVER_WHITELIST,
                                       String.join(",", whitelistedPackageNames));
         } catch (PackageManager.NameNotFoundException e) {
@@ -160,4 +211,48 @@
             }
         }
     }
+
+    private void processBlacklists() {
+        // TODO(b/121350991) Switch to DeviceConfig with property listener.
+        String base64String =
+                Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS);
+        if (base64String == null || base64String.isEmpty()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            // Reset all blacklists
+            mBlacklists = null;
+            try {
+                mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
+            } catch (IllegalArgumentException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            } catch (InvalidProtocolBufferException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            }
+        }
+    }
+
+    private void setBlacklist() {
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_BLACKLIST, "");
+        synchronized (mLock) {
+            if (mBlacklists == null) {
+                return;
+            }
+            List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
+            for (Blacklist blacklist : blacklists) {
+                if (blacklist.getVersionCode() == mGameDriverVersionCode) {
+                    Settings.Global.putString(mContentResolver,
+                            Settings.Global.GAME_DRIVER_BLACKLIST,
+                            String.join(",", blacklist.getPackageNamesList()));
+                    return;
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 20933db..560f7a0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -342,7 +342,7 @@
         super.disableDevice(initiatedByCec, callback);
 
         assertRunOnServiceThread();
-        if (!initiatedByCec && mIsActiveSource) {
+        if (!initiatedByCec && mIsActiveSource && mService.isControlEnabled()) {
             mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
                     mAddress, mService.getPhysicalAddress()));
         }
diff --git a/services/core/java/com/android/server/input/InputForwarder.java b/services/core/java/com/android/server/input/InputForwarder.java
deleted file mode 100644
index 00af839..0000000
--- a/services/core/java/com/android/server/input/InputForwarder.java
+++ /dev/null
@@ -1,45 +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.input;
-
-import android.app.IInputForwarder;
-import android.hardware.input.InputManagerInternal;
-import android.view.InputEvent;
-
-import com.android.server.LocalServices;
-
-import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
-
-/**
- * Basic implementation of {@link IInputForwarder}.
- */
-class InputForwarder extends IInputForwarder.Stub {
-
-    private final InputManagerInternal mInputManagerInternal;
-    private final int mDisplayId;
-
-    InputForwarder(int displayId) {
-        mDisplayId = displayId;
-        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
-    }
-
-    @Override
-    public boolean forwardEvent(InputEvent event) {
-        event.setDisplayId(mDisplayId);
-        return mInputManagerInternal.injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC);
-    }
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5e7ea05..87c7441 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,7 +17,6 @@
 package com.android.server.input;
 
 import android.annotation.NonNull;
-import android.app.IInputForwarder;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -1685,29 +1684,6 @@
         nativeMonitor(mPtr);
     }
 
-    // Binder call
-    @Override
-    public IInputForwarder createInputForwarder(int displayId) throws RemoteException {
-        if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
-                "createInputForwarder()")) {
-            throw new SecurityException("Requires INJECT_EVENTS permission");
-        }
-        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
-        final Display display = displayManager.getDisplay(displayId);
-        if (display == null) {
-            throw new IllegalArgumentException(
-                    "Can't create input forwarder for non-existent displayId: " + displayId);
-        }
-        final int callingUid = Binder.getCallingUid();
-        final int displayOwnerUid = display.getOwnerUid();
-        if (callingUid != displayOwnerUid) {
-            throw new SecurityException(
-                    "Only owner of the display can forward input events to it.");
-        }
-
-        return new InputForwarder(displayId);
-    }
-
     // Native callback.
     private void notifyConfigurationChanged(long whenNanos) {
         mWindowManagerCallbacks.notifyConfigurationChanged();
@@ -1805,9 +1781,10 @@
     }
 
     // Native callback.
-    private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+    private int interceptMotionBeforeQueueingNonInteractive(int displayId,
+            long whenNanos, int policyFlags) {
         return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
-                whenNanos, policyFlags);
+                displayId, whenNanos, policyFlags);
     }
 
     // Native callback.
@@ -2021,7 +1998,13 @@
 
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
-        public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
+        /**
+         * Provides an opportunity for the window manager policy to intercept early motion event
+         * processing when the device is in a non-interactive state since these events are normally
+         * dropped.
+         */
+        int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
+                int policyFlags);
 
         public long interceptKeyBeforeDispatching(IBinder token,
                 KeyEvent event, int policyFlags);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 2f0b388..243b6fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -373,6 +373,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    // Available only on GNSS HAL 2.0 implementations and later.
     private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
@@ -463,8 +464,8 @@
         }
     };
 
-    // TODO(b/119326010): replace OnSubscriptionsChangedListener with
-    // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver.
+    // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+    //       broadcast receiver.
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
@@ -676,8 +677,7 @@
         mNtpTimeHelper.onNetworkAvailable();
         if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
             if (mSupportsXtra) {
-                // Download only if supported, (prevents an unneccesary on-boot
-                // download)
+                // Download only if supported, (prevents an unnecessary on-boot download)
                 xtraDownloadRequest();
             }
         }
@@ -764,7 +764,7 @@
 
     /** Returns true if the location request is too frequent. */
     private boolean isRequestLocationRateLimited() {
-        // TODO(b/73198123): implement exponential backoff.
+        // TODO: implement exponential backoff.
         return false;
     }
 
@@ -917,7 +917,7 @@
         synchronized (mLock) {
             boolean enabled =
                     ((mProviderRequest != null && mProviderRequest.reportLocation
-                            && mProviderRequest.forceLocation) || (
+                            && mProviderRequest.locationSettingsIgnored) || (
                             mContext.getSystemService(LocationManager.class).isLocationEnabled()
                                     && !mDisableGps)) && !mShutdown;
             if (enabled == mEnabled) {
@@ -976,7 +976,7 @@
         }
 
         if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
-        if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) {
+        if (mProviderRequest.reportLocation && isEnabled()) {
             // update client uids
             updateClientUids(mWorkSource);
 
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index c3f25bf..20f872a 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -24,10 +24,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StatsLog;
@@ -70,7 +73,7 @@
     private final Handler mHandler;
     private final Context mContext;
 
-    private boolean mIsMasterLocationSettingsEnabled = true;
+    private boolean mIsDeviceLocationSettingsEnabled;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -88,13 +91,9 @@
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mPackageManager = mContext.getPackageManager();
 
-        // Set to empty proxy app list initially until the configuration properties are loaded.
-        updateNfwLocationAccessProxyAppsInGnssHal();
-
-        // Listen for proxy app package installation, removal events.
-        listenForProxyAppsPackageUpdates();
-
-        // TODO(b/122855984): Handle global location settings on/off.
+        // Complete initialization as the first event to run in mHandler thread. After that,
+        // all object state read/update events run in the mHandler thread.
+        runOnHandler(this::handleInitialize);
     }
 
     void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
@@ -105,10 +104,6 @@
         runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
 
-    void masterLocationSettingsUpdated(boolean enabled) {
-        runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled));
-    }
-
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
             boolean inEmergencyMode, boolean isCachedLocation) {
@@ -117,7 +112,19 @@
                         requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
     }
 
+    private void handleInitialize() {
+        disableNfwLocationAccess(); // Disable until config properties are loaded.
+        listenForProxyAppsPackageUpdates();
+        listenForDeviceLocationSettingsUpdate();
+        mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
+    }
+
+    private boolean getDeviceLocationSettings() {
+        return mContext.getSystemService(LocationManager.class).isLocationEnabled();
+    }
+
     private void listenForProxyAppsPackageUpdates() {
+        // Listen for proxy apps package installation, removal events.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -143,11 +150,22 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
     }
 
+    private void listenForDeviceLocationSettingsUpdate() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
+                true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        handleDeviceLocationSettingsUpdated();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
     private void handleProxyAppPackageUpdate(String pkgName, String action) {
         final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
-        // pkgName is not one of the proxy apps in our list.
         if (locationPermission == null) {
-            return;
+            return; // ignore, pkgName is not one of the proxy apps in our list.
         }
 
         Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action);
@@ -197,15 +215,33 @@
                 return true;
             }
         }
-
         return false;
     }
 
-    private void handleMasterLocationSettingsUpdated(boolean enabled) {
-        mIsMasterLocationSettingsEnabled = enabled;
-        Log.i(TAG, "Master location settings switch changed to "
-                + (enabled ? "enabled" : "disabled"));
-        updateNfwLocationAccessProxyAppsInGnssHal();
+    private void handleDeviceLocationSettingsUpdated() {
+        final boolean enabled = getDeviceLocationSettings();
+        Log.i(TAG, "Device location settings enabled: " + enabled);
+
+        if (mIsDeviceLocationSettingsEnabled == enabled) {
+            return;
+        }
+
+        mIsDeviceLocationSettingsEnabled = enabled;
+        if (!mIsDeviceLocationSettingsEnabled) {
+            disableNfwLocationAccess();
+            return;
+        }
+
+        // When device location settings was disabled, we already set the proxy app list
+        // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+        String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
+        if (locationPermissionEnabledProxyApps.length != 0) {
+            setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
+        }
+    }
+
+    private void disableNfwLocationAccess() {
+        setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS);
     }
 
     // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
@@ -316,8 +352,7 @@
             return mPackageManager.getApplicationInfo(pkgName, 0).uid;
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
-                Log.d(TAG, "Non-framework location access proxy app "
-                        + pkgName + " is not found.");
+                Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found.");
             }
             return null;
         }
@@ -329,8 +364,14 @@
     }
 
     private void updateNfwLocationAccessProxyAppsInGnssHal() {
-        final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess()
-                ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps();
+        if (!mIsDeviceLocationSettingsEnabled) {
+            return; // Keep non-framework location access disabled.
+        }
+        setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());
+    }
+
+    private void setNfwLocationAccessProxyAppsInGnssHal(
+            String[] locationPermissionEnabledProxyApps) {
         final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
         Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
                 + proxyAppsStr);
@@ -341,12 +382,8 @@
         }
     }
 
-    private boolean shouldDisableNfwLocationAccess() {
-        return !mIsMasterLocationSettingsEnabled;
-    }
-
     private String[] getLocationPermissionEnabledProxyApps() {
-        // Get a count of proxy apps with location permission enabled to array creation size.
+        // Get a count of proxy apps with location permission enabled for array creation size.
         int countLocationPermissionEnabledProxyApps = 0;
         for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
             if (hasLocationPermissionEnabled) {
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index f03c99b..aa8a25a 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -31,9 +31,11 @@
 import java.util.Map;
 
 /**
- * A helper class, that handles operations in remote listeners, and tracks for remote process death.
+ * A helper class that handles operations in remote listeners.
+ *
+ * @param <TListener> the type of GNSS data listener.
  */
-abstract class RemoteListenerHelper<TListener extends IInterface> {
+public abstract class RemoteListenerHelper<TListener extends IInterface> {
 
     protected static final int RESULT_SUCCESS = 0;
     protected static final int RESULT_NOT_AVAILABLE = 1;
@@ -46,7 +48,7 @@
     protected final Handler mHandler;
     private final String mTag;
 
-    private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+    private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
 
     protected final Context mContext;
     protected final AppOpsManager mAppOps;
@@ -71,24 +73,21 @@
         return mIsRegistered;
     }
 
-    public boolean addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+    /**
+     * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
+     */
+    public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
         Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
         IBinder binder = listener.asBinder();
-        LinkedListener deathListener = new LinkedListener(listener, callerIdentity);
         synchronized (mListenerMap) {
             if (mListenerMap.containsKey(binder)) {
                 // listener already added
-                return true;
+                return;
             }
-            try {
-                binder.linkToDeath(deathListener, 0 /* flags */);
-            } catch (RemoteException e) {
-                // if the remote process registering the listener is already death, just swallow the
-                // exception and return
-                Log.v(mTag, "Remote listener already died.", e);
-                return false;
-            }
-            mListenerMap.put(binder, deathListener);
+
+            IdentifiedListener identifiedListener = new IdentifiedListener(listener,
+                    callerIdentity);
+            mListenerMap.put(binder, identifiedListener);
 
             // update statuses we already know about, starting from the ones that will never change
             int result;
@@ -107,26 +106,23 @@
             } else {
                 // at this point if the supported flag is not set, the notification will be sent
                 // asynchronously in the future
-                return true;
+                return;
             }
-            post(deathListener, getHandlerOperation(result));
+            post(identifiedListener, getHandlerOperation(result));
         }
-        return true;
     }
 
+    /**
+     * Remove GNSS data listener {@code listener}.
+     */
     public void removeListener(@NonNull TListener listener) {
         Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
-        IBinder binder = listener.asBinder();
-        LinkedListener linkedListener;
         synchronized (mListenerMap) {
-            linkedListener = mListenerMap.remove(binder);
+            mListenerMap.remove(listener.asBinder());
             if (mListenerMap.isEmpty()) {
                 tryUnregister();
             }
         }
-        if (linkedListener != null) {
-            binder.unlinkToDeath(linkedListener, 0 /* flags */);
-        }
     }
 
     protected abstract boolean isAvailableInPlatform();
@@ -198,14 +194,15 @@
     }
 
     private void foreachUnsafe(ListenerOperation<TListener> operation) {
-        for (LinkedListener linkedListener : mListenerMap.values()) {
-            post(linkedListener, operation);
+        for (IdentifiedListener identifiedListener : mListenerMap.values()) {
+            post(identifiedListener, operation);
         }
     }
 
-    private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
+    private void post(IdentifiedListener identifiedListener,
+            ListenerOperation<TListener> operation) {
         if (operation != null) {
-            mHandler.post(new HandlerRunnable(linkedListener, operation));
+            mHandler.post(new HandlerRunnable(identifiedListener, operation));
         }
     }
 
@@ -259,35 +256,31 @@
         return RESULT_SUCCESS;
     }
 
-    private class LinkedListener implements IBinder.DeathRecipient {
+    private class IdentifiedListener {
         private final TListener mListener;
         private final CallerIdentity mCallerIdentity;
 
-        LinkedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+        private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
             mListener = listener;
             mCallerIdentity = callerIdentity;
         }
-
-        @Override
-        public void binderDied() {
-            Log.d(mTag, "Remote Listener died: " + mListener);
-            removeListener(mListener);
-        }
     }
 
     private class HandlerRunnable implements Runnable {
-        private final LinkedListener mLinkedListener;
+        private final IdentifiedListener mIdentifiedListener;
         private final ListenerOperation<TListener> mOperation;
 
-        HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
-            mLinkedListener = linkedListener;
+        private HandlerRunnable(IdentifiedListener identifiedListener,
+                ListenerOperation<TListener> operation) {
+            mIdentifiedListener = identifiedListener;
             mOperation = operation;
         }
 
         @Override
         public void run() {
             try {
-                mOperation.execute(mLinkedListener.mListener, mLinkedListener.mCallerIdentity);
+                mOperation.execute(mIdentifiedListener.mListener,
+                        mIdentifiedListener.mCallerIdentity);
             } catch (RemoteException e) {
                 Log.v(mTag, "Error in monitored listener.", e);
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a9ae74f..4b4788c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -55,6 +55,8 @@
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.FaceManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -671,7 +673,6 @@
         mDeviceProvisionedObserver.onSystemReady();
         // TODO: maybe skip this for split system user mode.
         mStorage.prefetchUser(UserHandle.USER_SYSTEM);
-        mStrongAuth.systemReady();
     }
 
     private void migrateOldData() {
@@ -2375,6 +2376,14 @@
             userCredential = null;
         }
 
+        final PackageManager pm = mContext.getPackageManager();
+        // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
+        // we need to generate challenge for each one, have it signed by GK and reset lockout
+        // for each modality.
+        if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
+        }
+
         final AuthenticationResult authResult;
         VerifyCredentialResponse response;
         synchronized (mSpManager) {
@@ -2413,6 +2422,17 @@
         if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
             notifyActivePasswordMetricsAvailable(userCredential, userId);
             unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
+            // Reset lockout
+            if (BiometricManager.hasBiometrics(mContext)) {
+                BiometricManager bm = mContext.getSystemService(BiometricManager.class);
+                Slog.i(TAG, "Resetting lockout, length: "
+                        + authResult.gkResponse.getPayload().length);
+                bm.resetLockout(authResult.gkResponse.getPayload());
+
+                if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+                    mContext.getSystemService(FaceManager.class).revokeChallenge();
+                }
+            }
 
             final byte[] secret = authResult.authToken.deriveDiskEncryptionKey();
             Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 4480435..a84306c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -16,15 +16,16 @@
 
 package com.android.server.locksettings;
 
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+        .STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+        .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteCallbackList;
@@ -61,7 +62,6 @@
     private final Context mContext;
 
     private AlarmManager mAlarmManager;
-    private BiometricManager mBiometricManager;
 
     public LockSettingsStrongAuth(Context context) {
         mContext = context;
@@ -69,12 +69,6 @@
         mAlarmManager = context.getSystemService(AlarmManager.class);
     }
 
-    public void systemReady() {
-        if (BiometricManager.hasBiometrics(mContext)) {
-            mBiometricManager = mContext.getSystemService(BiometricManager.class);
-        }
-    }
-
     private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
         mTrackers.register(tracker);
 
@@ -185,11 +179,6 @@
     }
 
     public void reportSuccessfulStrongAuthUnlock(int userId) {
-        if (mBiometricManager != null) {
-            byte[] token = null; /* TODO: pass real auth token once HAL supports it */
-            mBiometricManager.resetTimeout(token);
-        }
-
         final int argNotUsed = 0;
         mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
index c62a31e..ff22a8d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
@@ -20,6 +20,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import org.w3c.dom.Element;
+
 import java.security.SecureRandom;
 import java.security.cert.CertPath;
 import java.security.cert.X509Certificate;
@@ -28,8 +30,6 @@
 import java.util.Date;
 import java.util.List;
 
-import org.w3c.dom.Element;
-
 /**
  * Parses and holds the XML file containing the list of THM public-key certificates and related
  * metadata.
@@ -38,24 +38,20 @@
 
     private static final String METADATA_NODE_TAG = "metadata";
     private static final String METADATA_SERIAL_NODE_TAG = "serial";
-    private static final String METADATA_REFRESH_INTERVAL_NODE_TAG = "refresh-interval";
     private static final String ENDPOINT_CERT_LIST_TAG = "endpoints";
     private static final String ENDPOINT_CERT_ITEM_TAG = "cert";
     private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
     private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
 
     private final long serial;
-    private final long refreshInterval;
     private final List<X509Certificate> intermediateCerts;
     private final List<X509Certificate> endpointCerts;
 
     private CertXml(
             long serial,
-            long refreshInterval,
             List<X509Certificate> intermediateCerts,
             List<X509Certificate> endpointCerts) {
         this.serial = serial;
-        this.refreshInterval = refreshInterval;
         this.intermediateCerts = intermediateCerts;
         this.endpointCerts = endpointCerts;
     }
@@ -65,15 +61,6 @@
         return serial;
     }
 
-    /**
-     * Gets the refresh interval in the XML file containing public-key certificates. The refresh
-     * interval denotes the number of seconds that the client should follow to contact the server to
-     * refresh the XML file.
-     */
-    public long getRefreshInterval() {
-        return refreshInterval;
-    }
-
     @VisibleForTesting
     List<X509Certificate> getAllIntermediateCerts() {
         return intermediateCerts;
@@ -121,7 +108,6 @@
         Element rootNode = CertUtils.getXmlRootNode(bytes);
         return new CertXml(
                 parseSerial(rootNode),
-                parseRefreshInterval(rootNode),
                 parseIntermediateCerts(rootNode),
                 parseEndpointCerts(rootNode));
     }
@@ -136,16 +122,6 @@
         return Long.parseLong(contents.get(0));
     }
 
-    private static long parseRefreshInterval(Element rootNode) throws CertParsingException {
-        List<String> contents =
-                CertUtils.getXmlNodeContents(
-                        CertUtils.MUST_EXIST_EXACTLY_ONE,
-                        rootNode,
-                        METADATA_NODE_TAG,
-                        METADATA_REFRESH_INTERVAL_NODE_TAG);
-        return Long.parseLong(contents.get(0));
-    }
-
     private static List<X509Certificate> parseIntermediateCerts(Element rootNode)
             throws CertParsingException {
         List<String> contents =
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b6ef180..b221241 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1207,6 +1207,15 @@
             }
         }
 
+        public void setPlaybackSpeed(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, float speed) {
+            try {
+                mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+            }
+        }
+
         public void adjustVolume(String packageName, int pid, int uid,
                 ControllerCallbackLink caller, boolean asSystemService, int direction) {
             try {
@@ -1446,6 +1455,13 @@
         }
 
         @Override
+        public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+                float speed) {
+            mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    caller, speed);
+        }
+
+        @Override
         public void sendCustomAction(String packageName, ControllerCallbackLink caller,
                 String action, Bundle args) {
             mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
index 5c73178..62d9b20 100644
--- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -284,13 +284,16 @@
      * Tells the system UI that volume has changed on an active remote session.
      */
     public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
-        if (mRvc == null || !session.isActive()) {
-            return;
-        }
-        try {
-            mRvc.remoteVolumeChanged(session.getSessionToken(), flags);
-        } catch (Exception e) {
-            Log.wtf(TAG, "Error sending volume change to system UI.", e);
+        synchronized (mLock) {
+            if (mRvc == null || !session.isActive()) {
+                return;
+            }
+            try {
+                mRvc.remoteVolumeChanged(session.getSessionToken(), flags);
+            } catch (Exception e) {
+                Log.w(TAG, "Error sending volume change to system UI.", e);
+                mRvc = null;
+            }
         }
     }
 
@@ -563,7 +566,7 @@
             String callerPackageName, SessionCallbackLink cb, String tag) {
         FullUserRecord user = getFullUserRecordLocked(userId);
         if (user == null) {
-            Log.wtf(TAG, "Request from invalid user: " +  userId);
+            Log.w(TAG, "Request from invalid user: " +  userId + ", pkg=" + callerPackageName);
             throw new RuntimeException("Session request from invalid user.");
         }
 
@@ -643,7 +646,8 @@
                 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
                 mRvc.updateRemoteController(record == null ? null : record.getSessionToken());
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
+                Log.w(TAG, "Error sending default remote volume to sys ui.", e);
+                mRvc = null;
             }
         }
     }
@@ -1661,7 +1665,9 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 enforceSystemUiPermission("listen for volume changes", pid, uid);
-                mRvc = rvc;
+                synchronized (mLock) {
+                    mRvc = rvc;
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 9e5b92a..3f15b38 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -17,9 +17,6 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.provider.Settings.ACTION_VPN_SETTINGS;
 
 import android.app.Notification;
@@ -30,17 +27,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
-import android.net.LinkProperties;
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
-import android.net.NetworkPolicyManager;
 import android.os.INetworkManagementService;
-import android.os.RemoteException;
 import android.security.Credentials;
 import android.security.KeyStore;
-import android.system.Os;
 import android.text.TextUtils;
 import android.util.Slog;
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4bd8f45..6d82c1c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,15 +15,15 @@
  */
 package com.android.server.net;
 
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 
 import android.app.ActivityManager;
 import android.net.NetworkPolicyManager;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index af55605..75b62cb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,6 +38,11 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,12 +50,7 @@
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
 import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index e479a15..d0c59c1 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -320,6 +320,11 @@
     private final class PackageReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Slog.e(TAG, "Cannot handle package broadcast with null action");
+                return;
+            }
             final Uri data = intent.getData();
             if (data == null) {
                 Slog.e(TAG, "Cannot handle package broadcast with null data");
@@ -337,7 +342,7 @@
                 userIds = new int[] { UserHandle.getUserId(extraUid) };
             }
 
-            switch (intent.getAction()) {
+            switch (action) {
                 case ACTION_PACKAGE_ADDED:
                     if (replacing) {
                         onPackageUpgraded(packageName, userIds);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
new file mode 100644
index 0000000..8051abb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 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.s
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+import android.apex.ApexInfoList;
+import android.apex.ApexSessionInfo;
+import android.apex.IApexService;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * ApexManager class handles communications with the apex service to perform operation and queries,
+ * as well as providing caching to avoid unnecessary calls to the service.
+ */
+class ApexManager {
+    static final String TAG = "ApexManager";
+    private final IApexService mApexService;
+    private final Map<String, PackageInfo> mActivePackagesCache;
+
+    ApexManager() {
+        try {
+            mApexService = IApexService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow("apexservice"));
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("Required service apexservice not available");
+        }
+        mActivePackagesCache = populateActivePackagesCache();
+    }
+
+    @NonNull
+    private Map<String, PackageInfo> populateActivePackagesCache() {
+        try {
+            List<PackageInfo> list = new ArrayList<>();
+            final ApexInfo[] activePkgs = mApexService.getActivePackages();
+            for (ApexInfo ai : activePkgs) {
+                // If the device is using flattened APEX, don't report any APEX
+                // packages since they won't be managed or updated by PackageManager.
+                if ((new File(ai.packagePath)).isDirectory()) {
+                    break;
+                }
+                try {
+                    list.add(PackageParser.generatePackageInfoFromApex(
+                            new File(ai.packagePath), true /* collect certs */));
+                } catch (PackageParserException pe) {
+                    throw new IllegalStateException("Unable to parse: " + ai, pe);
+                }
+            }
+            return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity()));
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * Retrieves information about an active APEX package.
+     *
+     * @param packageName the package name to look for. Note that this is the package name reported
+     *                    in the APK container manifest (i.e. AndroidManifest.xml), which might
+     *                    differ from the one reported in the APEX manifest (i.e.
+     *                    apex_manifest.json).
+     * @return a PackageInfo object with the information about the package, or null if the package
+     *         is not found.
+     */
+    @Nullable PackageInfo getActivePackage(String packageName) {
+        return mActivePackagesCache.get(packageName);
+    }
+
+    /**
+     * Retrieves information about all active APEX packages.
+     *
+     * @return a Collection of PackageInfo object, each one containing information about a different
+     *         active package.
+     */
+    Collection<PackageInfo> getActivePackages() {
+        return mActivePackagesCache.values();
+    }
+
+    /**
+     * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
+     * track the different states of a session.
+     *
+     * @param sessionId the identifier of the session.
+     * @return an ApexSessionInfo object, or null if the session is not known.
+     */
+    @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
+        try {
+            ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
+            if (apexSessionInfo.isUnknown) {
+                return null;
+            }
+            return apexSessionInfo;
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * Submit a staged session to apex service. This causes the apex service to perform some initial
+     * verification and accept or reject the session. Submitting a session successfully is not
+     * enough for it to be activated at the next boot, the caller needs to call
+     * {@link #markStagedSessionReady(int)}.
+     *
+     * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
+     * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
+     *                        an array of identifiers of all the child sessions. Otherwise it should
+     *                        be an empty array.
+     * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller
+     *                     and will be filled with a list of {@link ApexInfo} objects, each of which
+     *                     contains metadata about one of the packages being submitted as part of
+     *                     the session.
+     * @return whether the submission of the session was successful.
+     */
+    boolean submitStagedSession(
+            int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) {
+        try {
+            return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
+     * applied at next reboot.
+     *
+     * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
+     * @return true upon success, false if the session is unknown.
+     */
+    boolean markStagedSessionReady(int sessionId) {
+        try {
+            return mApexService.markStagedSessionReady(sessionId);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * Marks a staged session as successful.
+     *
+     * <p>Only activated session can be marked as successful.
+     *
+     * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
+     *                  successful.
+     */
+    void markStagedSessionSuccessful(int sessionId) {
+        try {
+            mApexService.markStagedSessionSuccessful(sessionId);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            throw new RuntimeException(re);
+        } catch (Exception e) {
+            // It is fine to just log an exception in this case. APEXd will be able to recover in
+            // case markStagedSessionSuccessful fails.
+            Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
+        }
+    }
+
+    /**
+     * Dumps various state information to the provided {@link PrintWriter} object.
+     *
+     * @param pw the {@link PrintWriter} object to send information to.
+     * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
+     *                    information about that specific package will be dumped.
+     */
+    void dump(PrintWriter pw, @Nullable String packageName) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+        ipw.println();
+        ipw.println("Active APEX packages:");
+        ipw.increaseIndent();
+        try {
+            populateActivePackagesCache();
+            for (PackageInfo pi : mActivePackagesCache.values()) {
+                if (packageName != null && !packageName.equals(pi.packageName)) {
+                    continue;
+                }
+                ipw.println(pi.packageName);
+                ipw.increaseIndent();
+                ipw.println("Version: " + pi.versionCode);
+                ipw.println("Path: " + pi.applicationInfo.sourceDir);
+                ipw.decreaseIndent();
+            }
+            ipw.decreaseIndent();
+            ipw.println();
+            ipw.println("APEX session state:");
+            ipw.increaseIndent();
+            final ApexSessionInfo[] sessions = mApexService.getSessions();
+            for (ApexSessionInfo si : sessions) {
+                ipw.println("Session ID: " + si.sessionId);
+                ipw.increaseIndent();
+                if (si.isUnknown) {
+                    ipw.println("State: UNKNOWN");
+                } else if (si.isVerified) {
+                    ipw.println("State: VERIFIED");
+                } else if (si.isStaged) {
+                    ipw.println("State: STAGED");
+                } else if (si.isActivated) {
+                    ipw.println("State: ACTIVATED");
+                } else if (si.isActivationFailed) {
+                    ipw.println("State: ACTIVATION FAILED");
+                }
+                ipw.decreaseIndent();
+            }
+            ipw.decreaseIndent();
+        } catch (RemoteException e) {
+            ipw.println("Couldn't communicate with apexd.");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index 5b765df..d53d81c 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -28,7 +28,7 @@
 import android.util.EventLog;
 import android.util.Log;
 
-import com.android.server.pm.dex.DexLogger;
+import com.android.server.pm.dex.DynamicCodeLogger;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -38,9 +38,10 @@
 
 /**
  * Scheduled jobs related to logging of app dynamic code loading. The idle logging job runs daily
- * while idle and charging  and calls {@link DexLogger} to write dynamic code information to the
- * event log. The audit watching job scans the event log periodically while idle to find AVC audit
- * messages indicating use of dynamic native code and adds the information to {@link DexLogger}.
+ * while idle and charging  and calls {@link DynamicCodeLogger} to write dynamic code information
+ * to the event log. The audit watching job scans the event log periodically while idle to find AVC
+ * audit messages indicating use of dynamic native code and adds the information to
+ * {@link DynamicCodeLogger}.
  * {@hide}
  */
 public class DynamicCodeLoggingService extends JobService {
@@ -130,9 +131,9 @@
         }
     }
 
-    private static DexLogger getDexLogger() {
+    private static DynamicCodeLogger getDynamicCodeLogger() {
         PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
-        return pm.getDexManager().getDexLogger();
+        return pm.getDexManager().getDynamicCodeLogger();
     }
 
     private class IdleLoggingThread extends Thread {
@@ -149,14 +150,14 @@
                 Log.d(TAG, "Starting IdleLoggingJob run");
             }
 
-            DexLogger dexLogger = getDexLogger();
-            for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) {
+            DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger();
+            for (String packageName : dynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) {
                 if (mIdleLoggingStopRequested) {
                     Log.w(TAG, "Stopping IdleLoggingJob run at scheduler request");
                     return;
                 }
 
-                dexLogger.logDynamicCodeLoading(packageName);
+                dynamicCodeLogger.logDynamicCodeLoading(packageName);
             }
 
             jobFinished(mParams, /* reschedule */ false);
@@ -191,7 +192,7 @@
         private boolean processAuditEvents() {
             // Scan the event log for SELinux (avc) audit messages indicating when an
             // (untrusted) app has executed native code from an app data
-            // file. Matches are recorded in DexLogger.
+            // file. Matches are recorded in DynamicCodeLogger.
             //
             // These messages come from the kernel audit system via logd. (Note that
             // some devices may not generate these messages at all, or the format may
@@ -213,7 +214,7 @@
             // On each run we process all the matching events in the log. This may
             // mean re-processing events we have already seen, and in any case there
             // may be duplicate events for the same app+file. These are de-duplicated
-            // by DexLogger.
+            // by DynamicCodeLogger.
             //
             // Note that any app can write a message to the event log, including one
             // that looks exactly like an AVC audit message, so the information may
@@ -228,7 +229,7 @@
                     return true;
                 }
 
-                DexLogger dexLogger = getDexLogger();
+                DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger();
 
                 List<EventLog.Event> events = new ArrayList<>();
                 EventLog.readEvents(tags, events);
@@ -267,7 +268,7 @@
                         // hex-encodes the bytes; we need to undo that.
                         path = unhex(matcher.group(2));
                     }
-                    dexLogger.recordNative(uid, path);
+                    dynamicCodeLogger.recordNative(uid, path);
                 }
 
                 return true;
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 33b8641..8ce2568 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -9,6 +9,10 @@
 toddke@android.com
 toddke@google.com
 
+# apex support
+per-file ApexManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com
+per-file StagingManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com
+
 # dex
 per-file AbstractStatsBase.java = agampe@google.com
 per-file AbstractStatsBase.java = calin@google.com
@@ -19,6 +23,9 @@
 per-file CompilerStats.java = agampe@google.com
 per-file CompilerStats.java = calin@google.com
 per-file CompilerStats.java = ngeoffray@google.com
+per-file DynamicCodeLoggingService.java = agampe@google.com
+per-file DynamicCodeLoggingService.java = calin@google.com
+per-file DynamicCodeLoggingService.java = ngeoffray@google.com
 per-file InstructionSets.java = agampe@google.com
 per-file InstructionSets.java = calin@google.com
 per-file InstructionSets.java = ngeoffray@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6bd2a9..f5d88e3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,7 +84,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import libcore.io.IoUtils;
 
@@ -131,7 +131,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final StagingManager mStagingManager;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private AppOpsManager mAppOps;
 
@@ -186,10 +186,10 @@
         }
     };
 
-    public PackageInstallerService(Context context, PackageManagerService pm) {
+    public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) {
         mContext = context;
         mPm = pm;
-        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
+        mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
 
         mInstallThread = new HandlerThread(TAG);
         mInstallThread.start();
@@ -204,7 +204,7 @@
         mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
         mSessionsDir.mkdirs();
 
-        mStagingManager = new StagingManager(pm, this);
+        mStagingManager = new StagingManager(pm, this, am);
     }
 
     private void setBootCompleted()  {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 874d1a7..b1c186e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -119,13 +119,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.apex.ApexInfo;
-import android.apex.ApexSessionInfo;
-import android.apex.IApexService;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppDetailsActivity;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.ResourcesManager;
 import android.app.admin.IDevicePolicyManager;
@@ -240,7 +238,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
-import android.permission.PermissionControllerManager;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -292,7 +290,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
@@ -316,9 +313,9 @@
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -372,7 +369,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -446,8 +442,6 @@
     private static final boolean ENABLE_FREE_CACHE_V2 =
             SystemProperties.getBoolean("fw.free_cache_v2", true);
 
-    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
-
     private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
 
     private static final int RADIO_UID = Process.PHONE_UID;
@@ -733,10 +727,10 @@
     @GuardedBy("mPackages")
     final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
 
-    private PackageManager mPackageManager;
-
     private final ModuleInfoProvider mModuleInfoProvider;
 
+    private final ApexManager mApexManager;
+
     class PackageParserCallback implements PackageParser.Callback {
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -941,7 +935,7 @@
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private final ComponentResolver mComponentResolver;
     // List of packages names to keep cached, even if they are uninstalled for all users
@@ -986,6 +980,9 @@
     @GuardedBy("mPackages")
     private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider;
 
+    @GuardedBy("mPackages")
+    private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider;
+
     private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
         private Context mContext;
         private ComponentName mIntentFilterVerifierComponent;
@@ -1046,12 +1043,17 @@
             verificationIntent.setComponent(mIntentFilterVerifierComponent);
             verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
 
+            final long whitelistTimeout = getVerificationTimeout();
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setTemporaryAppWhitelistDuration(whitelistTimeout);
+
             DeviceIdleController.LocalService idleController = getDeviceIdleController();
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
-                    mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(),
+                    mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
                     UserHandle.USER_SYSTEM, true, "intent filter verifier");
 
-            mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM);
+            mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM,
+                    null, options.toBundle());
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                     "Sending IntentFilter verification broadcast");
         }
@@ -1345,7 +1347,7 @@
     final @Nullable String mRequiredVerifierPackage;
     final @NonNull String mRequiredInstallerPackage;
     final @NonNull String mRequiredUninstallerPackage;
-    final String mRequiredPermissionControllerPackage;
+    final @NonNull String mRequiredPermissionControllerPackage;
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mSystemTextClassifierPackage;
@@ -1933,9 +1935,14 @@
                             }
                         }
 
-                        // We may also need to apply pending (restored) runtime
-                        // permission grants within these users.
-                        mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+                        // We may also need to apply pending (restored) runtime permission grants
+                        // within these users.
+                        mPermissionManager.restoreDelayedRuntimePermissions(packageName,
+                                UserHandle.of(userId));
+
+                        // Persistent preferred activity might have came into effect due to this
+                        // install.
+                        updateDefaultHomeLPw(userId);
                     }
                 }
             }
@@ -3074,7 +3081,8 @@
                 }
             }
 
-            mInstallerService = new PackageInstallerService(context, this);
+            mApexManager = new ApexManager();
+            mInstallerService = new PackageInstallerService(context, this, mApexManager);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
@@ -3301,7 +3309,8 @@
         // feature flags should cause us to invalidate any caches.
         final String cacheName = SystemProperties.digestOf(
                 "ro.build.fingerprint",
-                "persist.sys.isolated_storage");
+                StorageManager.PROP_ISOLATED_STORAGE,
+                StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT);
 
         // Reconcile cache directories, keeping only what we'd actually use.
         for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -3934,27 +3943,7 @@
             }
             //
             if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) {
-                //TODO(b/123052859) Don't do file operations every time there is a query.
-                final IApexService apex = IApexService.Stub.asInterface(
-                        ServiceManager.getService("apexservice"));
-                if (apex != null) {
-                    try {
-                        final ApexInfo activePkg = apex.getActivePackage(packageName);
-                        if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) {
-                            try {
-                                return PackageParser.generatePackageInfoFromApex(
-                                        new File(activePkg.packagePath), true /* collect certs */);
-                            } catch (PackageParserException pe) {
-                                Log.e(TAG, "Unable to parse package at "
-                                        + activePkg.packagePath, pe);
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());
-                    }
-                } else {
-                    Log.e(TAG, "Unable to connect to apexservice for querying packages.");
-                }
+                return mApexManager.getActivePackage(packageName);
             }
         }
         return null;
@@ -7851,25 +7840,7 @@
             if (listApex) {
                 // TODO(b/119767311): include uninstalled/inactive APEX if
                 //  MATCH_UNINSTALLED_PACKAGES is set.
-                final IApexService apex = IApexService.Stub.asInterface(
-                        ServiceManager.getService("apexservice"));
-                if (apex != null) {
-                    try {
-                        final ApexInfo[] activePkgs = apex.getActivePackages();
-                        for (ApexInfo ai : activePkgs) {
-                            try {
-                                 list.add(PackageParser.generatePackageInfoFromApex(
-                                         new File(ai.packagePath), true /* collect certs */));
-                            } catch (PackageParserException pe) {
-                                 throw new IllegalStateException("Unable to parse: " + ai, pe);
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());
-                    }
-                } else {
-                    Log.e(TAG, "Unable to connect to apexservice for querying packages.");
-                }
+                list.addAll(mApexManager.getActivePackages());
             }
             return new ParceledListSlice<>(list);
         }
@@ -12836,7 +12807,7 @@
     public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
             int restrictionFlags, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
-                "setPackagesSuspendedAsUser");
+                "setDistractingPackageRestrictionsAsUser");
 
         final int callingUid = Binder.getCallingUid();
         if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
@@ -14604,14 +14575,19 @@
                             new BroadcastReceiver() {
                                 @Override
                                 public void onReceive(Context context, Intent intent) {
-                                    // TODO(ruhler) b/112431924 Have a configurable setting to
-                                    // allow changing the timeout and fall back to the default
-                                    // if no such specified.
+                                    // the duration to wait for rollback to be enabled, in millis
+                                    long rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT;
+                                    try {
+                                        rollbackTimeout = Long.valueOf(
+                                            DeviceConfig.getProperty(
+                                                DeviceConfig.Rollback.NAMESPACE,
+                                                DeviceConfig.Rollback.ENABLE_ROLLBACK_TIMEOUT));
+                                    } catch (NumberFormatException ignore) {
+                                    }
                                     final Message msg = mHandler.obtainMessage(
                                             ENABLE_ROLLBACK_TIMEOUT);
                                     msg.arg1 = enableRollbackToken;
-                                    mHandler.sendMessageDelayed(msg,
-                                            DEFAULT_ENABLE_ROLLBACK_TIMEOUT);
+                                    mHandler.sendMessageDelayed(msg, rollbackTimeout);
                                 }
                             }, null, 0, null, null);
 
@@ -18845,7 +18821,7 @@
             // permission as requiring a review as this is the initial state.
             int flags = 0;
             if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
-                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
             }
             if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
                 if (hasInstallState) {
@@ -19104,6 +19080,7 @@
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19254,6 +19231,13 @@
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     @GuardedBy("mPackages")
     boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
+        return clearPackagePreferredActivitiesLPw(packageName, false, userId);
+    }
+
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    @GuardedBy("mPackages")
+    private boolean clearPackagePreferredActivitiesLPw(String packageName,
+            boolean skipUpdateDefaultHome, int userId) {
         ArrayList<PreferredActivity> removed = null;
         boolean changed = false;
         for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
@@ -19282,6 +19266,9 @@
                     pir.removeFilter(pa);
                 }
                 changed = true;
+                if (!skipUpdateDefaultHome) {
+                    updateDefaultHomeLPw(thisUserId);
+                }
             }
         }
         if (changed) {
@@ -19341,8 +19328,9 @@
         // writer
         try {
             synchronized (mPackages) {
-                clearPackagePreferredActivitiesLPw(null, userId);
+                clearPackagePreferredActivitiesLPw(null, true, userId);
                 mSettings.applyDefaultPreferredAppsLPw(userId);
+                updateDefaultHomeLPw(userId);
                 // TODO: We have to reset the default SMS and Phone. This requires
                 // significant refactoring to keep all default apps in the package
                 // manager (cleaner but more work) or have the services provide
@@ -19411,6 +19399,7 @@
                     new PersistentPreferredActivity(filter, activity));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19454,6 +19443,7 @@
             if (changed) {
                 scheduleWritePackageRestrictionsLocked(userId);
                 postPreferredActivityChangedBroadcast(userId);
+                updateDefaultHomeLPw(userId);
             }
         }
     }
@@ -19541,6 +19531,7 @@
                     (readParser, readUserId) -> {
                         synchronized (mPackages) {
                             mSettings.readPreferredActivitiesLPw(readParser, readUserId);
+                            updateDefaultHomeLPw(readUserId);
                         }
                     });
         } catch (Exception e) {
@@ -19596,8 +19587,17 @@
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
                     (parser1, userId1) -> {
+                        String defaultBrowser;
                         synchronized (mPackages) {
                             mSettings.readDefaultAppsLPw(parser1, userId1);
+                            defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
+                        }
+                        if (defaultBrowser != null) {
+                            PackageManagerInternal.DefaultBrowserProvider provider;
+                            synchronized (mPackages) {
+                                provider = mDefaultBrowserProvider;
+                            }
+                            provider.setDefaultBrowser(defaultBrowser, userId1);
                         }
                     });
         } catch (Exception e) {
@@ -19661,139 +19661,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
-        }
-
-        AtomicReference<byte[]> backup = new AtomicReference<>();
-        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
-                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
-                    synchronized (backup) {
-                        backup.set(b);
-                        backup.notifyAll();
-                    }
-                });
-
-        long start = System.currentTimeMillis();
-        synchronized (backup) {
-            while (backup.get() == null) {
-                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
-                if (timeLeft <= 0) {
-                    return null;
-                }
-
-                try {
-                    backup.wait(timeLeft);
-                } catch (InterruptedException ignored) {
-                    return null;
-                }
-            }
-        }
-
-        return backup.get();
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call restorePermissionGrants()");
-        }
-
-        try {
-            final XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
-                    (parser1, userId1) -> {
-                        synchronized (mPackages) {
-                            processRestoredPermissionGrantsLPr(parser1, userId1);
-                        }
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
-            }
-        }
-    }
-
-    @GuardedBy("mPackages")
-    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
-            throws XmlPullParserException, IOException {
-        String pkgName = null;
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            final String tagName = parser.getName();
-            if (tagName.equals(TAG_GRANT)) {
-                pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
-                }
-            } else if (tagName.equals(TAG_PERMISSION)) {
-
-                final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
-                final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
-
-                int newFlagSet = 0;
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_SET;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_FIXED;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                    newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                }
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "  + Restoring grant:"
-                            + " pkg=" + pkgName
-                            + " perm=" + permName
-                            + " granted=" + isGranted
-                            + " bits=0x" + Integer.toHexString(newFlagSet));
-                }
-                final PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null) {
-                    // Already installed so we apply the grant immediately
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        + already installed; applying");
-                    }
-                    PermissionsState perms = ps.getPermissionsState();
-                    BasePermission bp =
-                            (BasePermission) mPermissionManager.getPermissionTEMP(permName);
-                    if (bp != null) {
-                        if (isGranted) {
-                            perms.grantRuntimePermission(bp, userId);
-                        }
-                        if (newFlagSet != 0) {
-                            perms.updatePermissionFlags(
-                                    bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
-                        }
-                    }
-                } else {
-                    // Need to wait for post-restore install to apply the grant
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        - not yet installed; saving for later");
-                    }
-                    mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
-                            isGranted, newFlagSet, userId);
-                }
-            } else {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
-                XmlUtils.skipCurrentTag(parser);
-            }
-        }
-
-        scheduleWriteSettingsLocked();
-        mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
-    @Override
     public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
             int sourceUserId, int targetUserId, int flags) {
         mContext.enforceCallingOrSelfPermission(
@@ -19955,19 +19822,59 @@
     ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
             int userId) {
         Intent intent  = getHomeIntent();
-        List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
                 PackageManager.GET_META_DATA, userId);
-        ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
-                true, false, false, userId);
-
         allHomeCandidates.clear();
-        if (list != null) {
-            allHomeCandidates.addAll(list);
+        if (resolveInfos == null) {
+            return null;
         }
-        return (preferred == null || preferred.activityInfo == null)
-                ? null
-                : new ComponentName(preferred.activityInfo.packageName,
-                        preferred.activityInfo.name);
+        allHomeCandidates.addAll(resolveInfos);
+
+        PackageManagerInternal.DefaultHomeProvider provider;
+        synchronized (mPackages) {
+            provider = mDefaultHomeProvider;
+        }
+        if (provider == null) {
+            Slog.e(TAG, "mDefaultHomeProvider is null");
+            return null;
+        }
+        String packageName = provider.getDefaultHome(userId);
+        if (packageName == null) {
+            return null;
+        }
+        int resolveInfosSize = resolveInfos.size();
+        for (int i = 0; i < resolveInfosSize; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+
+            if (resolveInfo.activityInfo != null && TextUtils.equals(
+                    resolveInfo.activityInfo.packageName, packageName)) {
+                return new ComponentName(resolveInfo.activityInfo.packageName,
+                        resolveInfo.activityInfo.name);
+            }
+        }
+        return null;
+    }
+
+    private void updateDefaultHomeLPw(int userId) {
+        Intent intent = getHomeIntent();
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
+                PackageManager.GET_META_DATA, userId);
+        ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos,
+                0, true, false, false, userId);
+        String packageName = preferredResolveInfo != null
+                && preferredResolveInfo.activityInfo != null
+                ? preferredResolveInfo.activityInfo.packageName : null;
+        String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId);
+        if (TextUtils.equals(currentPackageName, packageName)) {
+            return;
+        }
+        String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
+        if (callingPackages != null && ArrayUtils.contains(callingPackages,
+                mRequiredPermissionControllerPackage)) {
+            // PermissionController manages default home directly.
+            return;
+        }
+        mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId);
     }
 
     @Override
@@ -21241,10 +21148,6 @@
                 }
             }
 
-            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) {
-                mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
-            }
-
             if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
                 // XXX should handle packageName != null by dumping only install data that
                 // the given package is involved with.
@@ -21319,51 +21222,7 @@
         }
 
         if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
-            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
-            ipw.println();
-            ipw.println("Active APEX packages:");
-            ipw.increaseIndent();
-            final IApexService apex = IApexService.Stub.asInterface(
-                    ServiceManager.getService("apexservice"));
-            try {
-                final ApexInfo[] activeApexes = apex.getActivePackages();
-                for (ApexInfo ai : activeApexes) {
-                    if (packageName != null && !packageName.equals(ai.packageName)) {
-                        continue;
-                    }
-                    ipw.println(ai.packageName);
-                    ipw.increaseIndent();
-                    ipw.println("Version: " + Long.toString(ai.versionCode));
-                    ipw.println("Path: " + ai.packagePath);
-                    ipw.decreaseIndent();
-                }
-                ipw.decreaseIndent();
-                ipw.println();
-                ipw.println("APEX session state:");
-                ipw.increaseIndent();
-                final ApexSessionInfo[] sessions = apex.getSessions();
-                for (ApexSessionInfo si : sessions) {
-                    ipw.println("Session ID: " + Integer.toString(si.sessionId));
-                    ipw.increaseIndent();
-                    if (si.isUnknown) {
-                        ipw.println("State: UNKNOWN");
-                    } else if (si.isVerified) {
-                        ipw.println("State: VERIFIED");
-                    } else if (si.isStaged) {
-                        ipw.println("State: STAGED");
-                    } else if (si.isActivated) {
-                        ipw.println("State: ACTIVATED");
-                    } else if (si.isActivationPendingRetry) {
-                        ipw.println("State: ACTIVATION PENDING RETRY");
-                    } else if (si.isActivationFailed) {
-                        ipw.println("State: ACTIVATION FAILED");
-                    }
-                    ipw.decreaseIndent();
-                }
-                ipw.decreaseIndent();
-            } catch (RemoteException e) {
-                ipw.println("Couldn't communicate with apexd.");
-            }
+            mApexManager.dump(pw, packageName);
         }
     }
 
@@ -23901,6 +23760,13 @@
                 mDefaultBrowserProvider = provider;
             }
         }
+
+        @Override
+        public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) {
+            synchronized (mPackages) {
+                mDefaultHomeProvider = provider;
+            }
+        }
     }
 
     @GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 975ffb2..92fe377 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -19,10 +19,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
@@ -249,23 +245,6 @@
     private static final String ATTR_SDK_VERSION = "sdkVersion";
     private static final String ATTR_DATABASE_VERSION = "databaseVersion";
 
-    // Bookkeeping for restored permission grants
-    private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
-    // package name: ATTR_PACKAGE_NAME
-    private static final String TAG_PERMISSION_ENTRY = "perm";
-    // permission name: ATTR_NAME
-    // permission granted (boolean): ATTR_GRANTED
-    private static final String ATTR_USER_SET = "set";
-    private static final String ATTR_USER_FIXED = "fixed";
-    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
-    private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr";
-
-    // Flag mask of restored permission grants that are applied at install time
-    private static final int USER_RUNTIME_GRANT_MASK =
-            FLAG_PERMISSION_USER_SET
-            | FLAG_PERMISSION_USER_FIXED
-            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-
     private final Object mLock;
 
     private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -303,26 +282,6 @@
         int[] excludedUserIds;
     }
 
-    // Bookkeeping for restored user permission grants
-    final class RestoredPermissionGrant {
-        String permissionName;
-        boolean granted;
-        int grantBits;
-
-        RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) {
-            permissionName = name;
-            granted = isGranted;
-            grantBits = theGrantBits;
-        }
-    }
-
-    // This would be more compact as a flat array of restored grants or something, but we
-    // may have quite a few, especially during early device lifetime, and avoiding all those
-    // linear lookups will be important.
-    private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>
-            mRestoredUserGrants =
-                new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>();
-
     private static int mFirstAvailableUid = 0;
 
     /** Map from volume UUID to {@link VersionInfo} */
@@ -461,43 +420,6 @@
         return mRenamedPackages.put(pkgName, origPkgName);
     }
 
-    void applyPendingPermissionGrantsLPw(String packageName, int userId) {
-        ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                mRestoredUserGrants.get(userId);
-        if (grantsByPackage == null || grantsByPackage.size() == 0) {
-            return;
-        }
-
-        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName);
-        if (grants == null || grants.size() == 0) {
-            return;
-        }
-
-        final PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            Slog.e(TAG, "Can't find supposedly installed package " + packageName);
-            return;
-        }
-        final PermissionsState perms = ps.getPermissionsState();
-
-        for (RestoredPermissionGrant grant : grants) {
-            BasePermission bp = mPermissions.getPermission(grant.permissionName);
-            if (bp != null) {
-                if (grant.granted) {
-                    perms.grantRuntimePermission(bp, userId);
-                }
-                perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits);
-            }
-        }
-
-        // And remove it from the pending-grant bookkeeping
-        grantsByPackage.remove(packageName);
-        if (grantsByPackage.size() < 1) {
-            mRestoredUserGrants.remove(userId);
-        }
-        writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
     public boolean canPropagatePermissionToInstantApp(String permName) {
         return mPermissions.canPropagatePermissionToInstantApp(permName);
     }
@@ -1982,13 +1904,6 @@
         }
     }
 
-    // Specifically for backup/restore
-    public void processRestoredPermissionGrantLPr(String pkgName, String permission,
-            boolean isGranted, int restoredFlagSet, int userId) {
-        mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
-                pkgName, permission, isGranted, restoredFlagSet, userId);
-    }
-
     void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
             throws IllegalArgumentException, IllegalStateException, IOException {
         serializer.startTag(null, TAG_DEFAULT_APPS);
@@ -5014,51 +4929,6 @@
         pw.print(mReadMessages.toString());
     }
 
-    void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) {
-        if (mRestoredUserGrants.size() > 0) {
-            pw.println();
-            pw.println("Restored (pending) permission grants:");
-            for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) {
-                ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                        mRestoredUserGrants.valueAt(userIndex);
-                if (grantsByPackage != null && grantsByPackage.size() > 0) {
-                    final int userId = mRestoredUserGrants.keyAt(userIndex);
-                    pw.print("  User "); pw.println(userId);
-
-                    for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) {
-                        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex);
-                        if (grants != null && grants.size() > 0) {
-                            final String pkgName = grantsByPackage.keyAt(pkgIndex);
-                            pw.print("    "); pw.print(pkgName); pw.println(" :");
-
-                            for (RestoredPermissionGrant g : grants) {
-                                pw.print("      ");
-                                pw.print(g.permissionName);
-                                if (g.granted) {
-                                    pw.print(" GRANTED");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                    pw.print(" user_set");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                    pw.print(" user_fixed");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                    pw.print(" revoke_on_upgrade");
-                                }
-                                if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
-                                    pw.print(" revoke_when_requested");
-                                }
-                                pw.println();
-                            }
-                        }
-                    }
-                }
-            }
-            pw.println();
-        }
-    }
-
     private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
         if (pkg == null) {
             pw.print("unknown");
@@ -5328,55 +5198,6 @@
 
                 serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
 
-                // Now any restored permission grants that are waiting for the apps
-                // in question to be installed.  These are stored as per-package
-                // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
-                // number of individual permission grant entities.
-                if (mRestoredUserGrants.get(userId) != null) {
-                    ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants =
-                            mRestoredUserGrants.get(userId);
-                    if (restoredGrants != null) {
-                        final int pkgCount = restoredGrants.size();
-                        for (int i = 0; i < pkgCount; i++) {
-                            final ArraySet<RestoredPermissionGrant> pkgGrants =
-                                    restoredGrants.valueAt(i);
-                            if (pkgGrants != null && pkgGrants.size() > 0) {
-                                final String pkgName = restoredGrants.keyAt(i);
-                                serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                                serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);
-
-                                final int N = pkgGrants.size();
-                                for (int z = 0; z < N; z++) {
-                                    RestoredPermissionGrant g = pkgGrants.valueAt(z);
-                                    serializer.startTag(null, TAG_PERMISSION_ENTRY);
-                                    serializer.attribute(null, ATTR_NAME, g.permissionName);
-
-                                    if (g.granted) {
-                                        serializer.attribute(null, ATTR_GRANTED, "true");
-                                    }
-
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                        serializer.attribute(null, ATTR_USER_SET, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                        serializer.attribute(null, ATTR_USER_FIXED, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                                    }
-                                    if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
-                                            != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED,
-                                                "true");
-                                    }
-                                    serializer.endTag(null, TAG_PERMISSION_ENTRY);
-                                }
-                                serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                            }
-                        }
-                    }
-                }
-
                 serializer.endDocument();
                 destination.finishWrite(out);
 
@@ -5455,29 +5276,6 @@
             }
         }
 
-        // Backup/restore support
-
-        public void rememberRestoredUserGrantLPr(String pkgName, String permission,
-                boolean isGranted, int restoredFlagSet, int userId) {
-            // This change will be remembered at write-settings time
-            ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                    mRestoredUserGrants.get(userId);
-            if (grantsByPackage == null) {
-                grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>();
-                mRestoredUserGrants.put(userId, grantsByPackage);
-            }
-
-            ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName);
-            if (grants == null) {
-                grants = new ArraySet<RestoredPermissionGrant>();
-                grantsByPackage.put(pkgName, grants);
-            }
-
-            RestoredPermissionGrant grant = new RestoredPermissionGrant(permission,
-                    isGranted, restoredFlagSet);
-            grants.add(grant);
-        }
-
         // Private internals
 
         @GuardedBy("Settings.this.mLock")
@@ -5520,50 +5318,6 @@
                         }
                         parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
                     } break;
-
-                    case TAG_RESTORED_RUNTIME_PERMISSIONS: {
-                        final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                        parseRestoredRuntimePermissionsLPr(parser, pkgName, userId);
-                    } break;
-                }
-            }
-        }
-
-        private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser,
-                final String pkgName, final int userId) throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-
-                switch (parser.getName()) {
-                    case TAG_PERMISSION_ENTRY: {
-                        final String permName = parser.getAttributeValue(null, ATTR_NAME);
-                        final boolean isGranted = "true".equals(
-                                parser.getAttributeValue(null, ATTR_GRANTED));
-
-                        int permBits = 0;
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                            permBits |= FLAG_PERMISSION_USER_SET;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                            permBits |= FLAG_PERMISSION_USER_FIXED;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null,
-                                ATTR_REVOKE_WHEN_REQUESTED))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-                        }
-
-                        if (isGranted || permBits != 0) {
-                            rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
-                        }
-                    } break;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 84c8b60..d9a5eb9 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.Person;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -33,6 +34,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.ShortcutService.DumpFilter;
@@ -71,6 +73,7 @@
     private static final String TAG_EXTRAS = "extras";
     private static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_CATEGORIES = "categories";
+    private static final String TAG_PERSON = "person";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_CALL_COUNT = "call-count";
@@ -96,6 +99,12 @@
     private static final String ATTR_ICON_RES_NAME = "icon-resname";
     private static final String ATTR_BITMAP_PATH = "bitmap-path";
 
+    private static final String ATTR_PERSON_NAME = "name";
+    private static final String ATTR_PERSON_URI = "uri";
+    private static final String ATTR_PERSON_KEY = "key";
+    private static final String ATTR_PERSON_IS_BOT = "is-bot";
+    private static final String ATTR_PERSON_IS_IMPORTANT = "is-important";
+
     private static final String NAME_CATEGORIES = "categories";
 
     private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
@@ -1499,6 +1508,22 @@
                     out.endTag(null, TAG_CATEGORIES);
                 }
             }
+            if (!forBackup) {  // Don't backup the persons field.
+                final Person[] persons = si.getPersons();
+                if (!ArrayUtils.isEmpty(persons)) {
+                    for (int i = 0; i < persons.length; i++) {
+                        final Person p = persons[i];
+
+                        out.startTag(null, TAG_PERSON);
+                        ShortcutService.writeAttr(out, ATTR_PERSON_NAME, p.getName());
+                        ShortcutService.writeAttr(out, ATTR_PERSON_URI, p.getUri());
+                        ShortcutService.writeAttr(out, ATTR_PERSON_KEY, p.getKey());
+                        ShortcutService.writeAttr(out, ATTR_PERSON_IS_BOT, p.isBot());
+                        ShortcutService.writeAttr(out, ATTR_PERSON_IS_IMPORTANT, p.isImportant());
+                        out.endTag(null, TAG_PERSON);
+                    }
+                }
+            }
             final Intent[] intentsNoExtras = si.getIntentsNoExtras();
             final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
             final int numIntents = intentsNoExtras.length;
@@ -1588,6 +1613,7 @@
         String bitmapPath;
         int backupVersionCode;
         ArraySet<String> categories = null;
+        ArrayList<Person> persons = new ArrayList<>();
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -1638,6 +1664,9 @@
                 case TAG_CATEGORIES:
                     // This just contains string-array.
                     continue;
+                case TAG_PERSON:
+                    persons.add(parsePerson(parser));
+                    continue;
                 case TAG_STRING_ARRAY_XMLUTILS:
                     if (NAME_CATEGORIES.equals(ShortcutService.parseStringAttribute(parser,
                             ATTR_NAME_XMLUTILS))) {
@@ -1680,7 +1709,8 @@
                 categories,
                 intents.toArray(new Intent[intents.size()]),
                 rank, extras, lastChangedTimestamp, flags,
-                iconResId, iconResName, bitmapPath, disabledReason);
+                iconResId, iconResName, bitmapPath, disabledReason,
+                persons.toArray(new Person[persons.size()]));
     }
 
     private static Intent parseIntent(XmlPullParser parser)
@@ -1713,6 +1743,20 @@
         return intent;
     }
 
+    private static Person parsePerson(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME);
+        String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI);
+        String key = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_KEY);
+        boolean isBot = ShortcutService.parseBooleanAttribute(parser, ATTR_PERSON_IS_BOT);
+        boolean isImportant = ShortcutService.parseBooleanAttribute(parser,
+                ATTR_PERSON_IS_IMPORTANT);
+
+        Person.Builder builder = new Person.Builder();
+        builder.setName(name).setUri(uri).setKey(key).setBot(isBot).setImportant(isImportant);
+        return builder.build();
+    }
+
     @VisibleForTesting
     List<ShortcutInfo> getAllShortcutsForTest() {
         return new ArrayList<>(mShortcuts.values());
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 90f08c3..668fc88 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -449,7 +449,8 @@
                 iconResId,
                 null, // icon res name
                 null, // bitmap path
-                disabledReason);
+                disabledReason,
+                null /* persons */);
     }
 
     private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index fa8360b..9dfd477 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -20,12 +20,12 @@
 import android.apex.ApexInfo;
 import android.apex.ApexInfoList;
 import android.apex.ApexSessionInfo;
-import android.apex.IApexService;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
@@ -41,7 +41,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.apk.ApkSignatureVerifier;
@@ -68,14 +67,16 @@
 
     private final PackageInstallerService mPi;
     private final PackageManagerService mPm;
+    private final ApexManager mApexManager;
     private final Handler mBgHandler;
 
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
 
-    StagingManager(PackageManagerService pm, PackageInstallerService pi) {
+    StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) {
         mPm = pm;
         mPi = pi;
+        mApexManager = am;
         mBgHandler = BackgroundThread.getHandler();
     }
 
@@ -100,7 +101,7 @@
         return new ParceledListSlice<>(result);
     }
 
-    private static boolean validateApexSignature(String apexPath, String packageName) {
+    private boolean validateApexSignature(String apexPath, String packageName) {
         final SigningDetails signingDetails;
         try {
             signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
@@ -109,17 +110,9 @@
             return false;
         }
 
-        final IApexService apex = IApexService.Stub.asInterface(
-                ServiceManager.getService("apexservice"));
-        final ApexInfo apexInfo;
-        try {
-            apexInfo = apex.getActivePackage(packageName);
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to contact APEXD", re);
-            return false;
-        }
+        final PackageInfo packageInfo = mApexManager.getActivePackage(packageName);
 
-        if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) {
+        if (packageInfo == null) {
             // TODO: What is the right thing to do here ? This implies there's no active package
             // with the given name. This should never be the case in production (where we only
             // accept updates to existing APEXes) but may be required for testing.
@@ -129,9 +122,10 @@
         final SigningDetails existingSigningDetails;
         try {
             existingSigningDetails = ApkSignatureVerifier.verify(
-                apexInfo.packagePath, SignatureSchemeVersion.JAR);
+                packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
         } catch (PackageParserException e) {
-            Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e);
+            Slog.e(TAG, "Unable to parse APEX package: "
+                    + packageInfo.applicationInfo.sourceDir, e);
             return false;
         }
 
@@ -143,10 +137,10 @@
         return false;
     }
 
-    private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session,
-                                                      List<PackageInstallerSession> childSessions,
-                                                      ApexInfoList apexInfoList) {
-        return sendSubmitStagedSessionRequest(
+    private boolean submitSessionToApexService(@NonNull PackageInstallerSession session,
+                                               List<PackageInstallerSession> childSessions,
+                                               ApexInfoList apexInfoList) {
+        return mApexManager.submitStagedSession(
                 session.sessionId,
                 childSessions != null
                         ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() :
@@ -154,33 +148,6 @@
                 apexInfoList);
     }
 
-    private static boolean sendSubmitStagedSessionRequest(
-            int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) {
-        final IApexService apex = IApexService.Stub.asInterface(
-                ServiceManager.getService("apexservice"));
-        boolean success;
-        try {
-            success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList);
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to contact apexservice", re);
-            return false;
-        }
-        return success;
-    }
-
-    private static boolean sendMarkStagedSessionReadyRequest(int sessionId) {
-        final IApexService apex = IApexService.Stub.asInterface(
-                ServiceManager.getService("apexservice"));
-        boolean success;
-        try {
-            success = apex.markStagedSessionReady(sessionId);
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to contact apexservice", re);
-            return false;
-        }
-        return success;
-    }
-
     private static boolean isApexSession(@NonNull PackageInstallerSession session) {
         return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
     }
@@ -189,12 +156,8 @@
         boolean success = true;
 
         // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
-        if (!sessionContainsApex(session)) {
-            // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
-            // right away.
-            session.setStagedSessionReady();
-            return;
-        }
+        // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+        // right away.
 
         final ApexInfoList apexInfoList = new ApexInfoList();
         // APEX checks. For single-package sessions, check if they contain an APEX. For
@@ -260,7 +223,8 @@
         }
 
         session.setStagedSessionReady();
-        if (!sendMarkStagedSessionReadyRequest(session.sessionId)) {
+        if (sessionContainsApex(session)
+                && !mApexManager.markStagedSessionReady(session.sessionId)) {
             session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                             "APEX staging failed, check logcat messages from apexd for more "
                             + "details.");
@@ -283,17 +247,14 @@
     }
 
     private void resumeSession(@NonNull PackageInstallerSession session) {
-        if (sessionContainsApex(session)) {
-            // Check with apexservice whether the apex
-            // packages have been activated.
-            final IApexService apex = IApexService.Stub.asInterface(
-                    ServiceManager.getService("apexservice"));
-            ApexSessionInfo apexSessionInfo;
-            try {
-                apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to contact apexservice", re);
-                // TODO should we retry here? Mark the session as failed?
+        boolean hasApex = sessionContainsApex(session);
+        if (hasApex) {
+            // Check with apexservice whether the apex packages have been activated.
+            ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+            if (apexSessionInfo == null) {
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "apexd did not know anything about a staged session supposed to be"
+                        + "activated");
                 return;
             }
             if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
@@ -311,7 +272,7 @@
                 mBgHandler.post(() -> preRebootVerification(session));
                 return;
             }
-            if (!apexSessionInfo.isActivated) {
+            if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
                 // In all the remaining cases apexd will try to apply the session again at next
                 // boot. Nothing to do here for now.
                 Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
@@ -323,37 +284,40 @@
         // The APEX part of the session is activated, proceed with the installation of APKs.
         if (!installApksInSession(session)) {
             session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                    "APEX activation failed. Check logcat messages from apexd for "
-                            + "more information.");
+                    "Staged installation of APKs failed. Check logcat messages for"
+                        + "more information.");
             return;
         }
+
         session.setStagedSessionApplied();
+        if (hasApex) {
+            mApexManager.markStagedSessionSuccessful(session.sessionId);
+        }
     }
 
-    private String findFirstAPKInDir(File stageDir) {
+    private List<String> findAPKsInDir(File stageDir) {
+        List<String> ret = new ArrayList<>();
         if (stageDir != null && stageDir.exists()) {
             for (File file : stageDir.listFiles()) {
                 if (file.getAbsolutePath().toLowerCase().endsWith(".apk")) {
-                    return file.getAbsolutePath();
+                    ret.add(file.getAbsolutePath());
                 }
             }
         }
-        return null;
+        return ret;
     }
 
     private PackageInstallerSession createAndWriteApkSession(
             @NonNull PackageInstallerSession originalSession) {
-        // TODO(b/123629153): support split APKs.
         if (originalSession.stageDir == null) {
             Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
             return null;
         }
-        String apkFilePath = findFirstAPKInDir(originalSession.stageDir);
-        if (apkFilePath == null) {
+        List<String> apkFilePaths = findAPKsInDir(originalSession.stageDir);
+        if (apkFilePaths.isEmpty()) {
             Slog.w(TAG, "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath());
             return null;
         }
-        File apkFile = new File(apkFilePath);
 
         PackageInstaller.SessionParams params = originalSession.params.copy();
         params.isStaged = false;
@@ -364,14 +328,17 @@
 
         try {
             apkSession.open();
-            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
-                    ParcelFileDescriptor.MODE_READ_ONLY);
-            long sizeBytes = pfd.getStatSize();
-            if (sizeBytes < 0) {
-                Slog.e(TAG, "Unable to get size of: " + apkFilePath);
-                return null;
+            for (String apkFilePath : apkFilePaths) {
+                File apkFile = new File(apkFilePath);
+                ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
+                        ParcelFileDescriptor.MODE_READ_ONLY);
+                long sizeBytes = pfd.getStatSize();
+                if (sizeBytes < 0) {
+                    Slog.e(TAG, "Unable to get size of: " + apkFilePath);
+                    return null;
+                }
+                apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
             }
-            apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
         } catch (IOException e) {
             Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
             return null;
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index a2e98cc..24b38e7 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -1,4 +1,15 @@
 {
+  "presubmit": [
+    {
+      "name": "CtsUsesLibraryHostTestCases"
+    },
+    {
+      "name": "CtsClassloaderSplitsHostTestCases"
+    },
+    {
+      "name": "CtsCompilationTestCases"
+    }
+  ],
   "imports": [
     {
       "path": "system/apex/tests"
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index ee6995b..3b805d51 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -86,11 +86,11 @@
     // encode and save the dex usage data.
     private final PackageDexUsage mPackageDexUsage;
 
-    // DexLogger handles recording of dynamic code loading - which is similar to PackageDexUsage
-    // but records a different aspect of the data.
+    // DynamicCodeLogger handles recording of dynamic code loading - which is similar to
+    // PackageDexUsage but records a different aspect of the data.
     // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
     // record class loaders or ISAs.)
-    private final DexLogger mDexLogger;
+    private final DynamicCodeLogger mDynamicCodeLogger;
 
     private final IPackageManager mPackageManager;
     private final PackageDexOptimizer mPackageDexOptimizer;
@@ -126,11 +126,11 @@
         mPackageDexOptimizer = pdo;
         mInstaller = installer;
         mInstallLock = installLock;
-        mDexLogger = new DexLogger(pms, installer);
+        mDynamicCodeLogger = new DynamicCodeLogger(pms, installer);
     }
 
-    public DexLogger getDexLogger() {
-        return mDexLogger;
+    public DynamicCodeLogger getDynamicCodeLogger() {
+        return mDynamicCodeLogger;
     }
 
     /**
@@ -230,8 +230,8 @@
 
                 if (!primaryOrSplit) {
                     // Record loading of a DEX file from an app data directory.
-                    mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName,
-                            loadingAppInfo.packageName);
+                    mDynamicCodeLogger.recordDex(loaderUserId, dexPath,
+                            searchResult.mOwningPackageName, loadingAppInfo.packageName);
                 }
 
                 if (classLoaderContexts != null) {
@@ -269,7 +269,7 @@
             loadInternal(existingPackages);
         } catch (Exception e) {
             mPackageDexUsage.clear();
-            mDexLogger.clear();
+            mDynamicCodeLogger.clear();
             Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
         }
     }
@@ -320,12 +320,12 @@
             if (mPackageDexUsage.removePackage(packageName)) {
                 mPackageDexUsage.maybeWriteAsync();
             }
-            mDexLogger.removePackage(packageName);
+            mDynamicCodeLogger.removePackage(packageName);
         } else {
             if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
                 mPackageDexUsage.maybeWriteAsync();
             }
-            mDexLogger.removeUserPackage(packageName, userId);
+            mDynamicCodeLogger.removeUserPackage(packageName, userId);
         }
     }
 
@@ -404,9 +404,9 @@
         }
 
         try {
-            mDexLogger.readAndSync(packageToUsersMap);
+            mDynamicCodeLogger.readAndSync(packageToUsersMap);
         } catch (Exception e) {
-            mDexLogger.clear();
+            mDynamicCodeLogger.clear();
             Slog.w(TAG, "Exception while loading package dynamic code usage. "
                     + "Starting with a fresh state.", e);
         }
@@ -692,7 +692,7 @@
      */
     public void writePackageDexUsageNow() {
         mPackageDexUsage.writeNow();
-        mDexLogger.writeNow();
+        mDynamicCodeLogger.writeNow();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java
similarity index 95%
rename from services/core/java/com/android/server/pm/dex/DexLogger.java
rename to services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java
index 59cc0cf..2c75bcd 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.pm.dex;
@@ -44,12 +44,12 @@
 import java.util.Set;
 
 /**
- * This class is responsible for logging data about secondary dex files and, despite the name,
- * native code executed from an app's private directory. The data logged includes hashes of the
- * name and content of each file.
+ * This class is responsible for logging data about secondary dex files and native code executed
+ * from an app's private directory. The data logged includes hashes of the name and content of each
+ * file.
  */
-public class DexLogger {
-    private static final String TAG = "DexLogger";
+public class DynamicCodeLogger {
+    private static final String TAG = "DynamicCodeLogger";
 
     // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) -
     // see b/63927552.
@@ -61,12 +61,12 @@
     private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
     private final Installer mInstaller;
 
-    public DexLogger(IPackageManager pms, Installer installer) {
+    DynamicCodeLogger(IPackageManager pms, Installer installer) {
         this(pms, installer, new PackageDynamicCodeLoading());
     }
 
     @VisibleForTesting
-    DexLogger(IPackageManager pms, Installer installer,
+    DynamicCodeLogger(IPackageManager pms, Installer installer,
             PackageDynamicCodeLoading packageDynamicCodeLoading) {
         mPackageManager = pms;
         mPackageDynamicCodeLoading = packageDynamicCodeLoading;
@@ -217,7 +217,7 @@
 
     /**
      * Record that an app running in the specified uid has executed native code from the file at
-     * {@link path}.
+     * {@param path}.
      */
     public void recordNative(int loadingUid, String path) {
         String[] packages;
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 2036ed7..dacc6cd 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -721,7 +721,7 @@
         grantSystemFixedPermissionsToSystemPackage(
                 getDefaultSystemHandlerActivityPackage(
                         RingtoneManager.ACTION_RINGTONE_PICKER, userId),
-                userId, STORAGE_PERMISSIONS);
+                userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
 
         // TextClassifier Service
         String textClassifierPackageName =
@@ -1462,14 +1462,15 @@
                         outGrantExceptions.get(packageName);
                 if (packageExceptions == null) {
                     // The package must be on the system image
-                    if (!isSystemPackage(packageName)) {
-                        Log.w(TAG, "Unknown package:" + packageName);
+                    PackageInfo packageInfo = getSystemPackageInfo(packageName);
+                    if (!isSystemPackage(packageInfo)) {
+                        Log.w(TAG, "Unknown system package:" + packageName);
                         XmlUtils.skipCurrentTag(parser);
                         continue;
                     }
 
                     // The package must support runtime permissions
-                    if (!doesPackageSupportRuntimePermissions(getSystemPackageInfo(packageName))) {
+                    if (!doesPackageSupportRuntimePermissions(packageInfo)) {
                         Log.w(TAG, "Skipping non supporting runtime permissions package:"
                                 + packageName);
                         XmlUtils.skipCurrentTag(parser);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 38940d6..8df5a71 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -30,6 +30,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -43,6 +44,9 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -69,7 +73,9 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
+import android.permission.PermissionManagerInternal;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -77,6 +83,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -91,9 +98,8 @@
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.SharedUserSetting;
 import com.android.server.pm.UserManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
-        .DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState.PermissionState;
 
 import libcore.util.EmptyArray;
@@ -106,6 +112,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -122,6 +132,8 @@
     /** Permission grant: grant as runtime a permission that was granted as an install time one. */
     private static final int GRANT_UPGRADE = 4;
 
+    private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
+
     /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
     /** Empty array to avoid allocations */
@@ -146,6 +158,9 @@
     /** Internal connection to the user manager */
     private final UserManagerInternal mUserManagerInt;
 
+    /** Permission controller: User space permission management */
+    private PermissionControllerManager mPermissionControllerManager;
+
     /** Default permission policy to provide proper behaviour out-of-the-box */
     private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
 
@@ -180,6 +195,16 @@
     @GuardedBy("mLock")
     private ArrayMap<String, List<String>> mBackgroundPermissions;
 
+    /**
+     * A permission backup might contain apps that are not installed. In this case we delay the
+     * restoration until the app is installed.
+     *
+     * <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where
+     * there is <u>no more</u> delayed backup left.
+     */
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray();
+
     PermissionManagerService(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
@@ -218,29 +243,31 @@
             }
         }
 
-        LocalServices.addService(
-                PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+        PermissionManagerServiceInternalImpl localService =
+                new PermissionManagerServiceInternalImpl();
+        LocalServices.addService(PermissionManagerServiceInternal.class, localService);
+        LocalServices.addService(PermissionManagerInternal.class, localService);
     }
 
     /**
      * Creates and returns an initialized, internal service for use by other components.
      * <p>
      * The object returned is identical to the one returned by the LocalServices class using:
-     * {@code LocalServices.getService(PermissionManagerInternal.class);}
+     * {@code LocalServices.getService(PermissionManagerServiceInternal.class);}
      * <p>
      * NOTE: The external lock is temporary and should be removed. This needs to be a
      * lock created by the permission manager itself.
      */
-    public static PermissionManagerInternal create(Context context,
+    public static PermissionManagerServiceInternal create(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
-        final PermissionManagerInternal permMgrInt =
-                LocalServices.getService(PermissionManagerInternal.class);
+        final PermissionManagerServiceInternal permMgrInt =
+                LocalServices.getService(PermissionManagerServiceInternal.class);
         if (permMgrInt != null) {
             return permMgrInt;
         }
         new PermissionManagerService(context, defaultGrantCallback, externalLock);
-        return LocalServices.getService(PermissionManagerInternal.class);
+        return LocalServices.getService(PermissionManagerServiceInternal.class);
     }
 
     @Nullable BasePermission getPermission(String permName) {
@@ -332,6 +359,74 @@
     }
 
     /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+        CompletableFuture<byte[]> backup = new CompletableFuture<>();
+        mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(),
+                backup::complete);
+
+        try {
+            return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException  | TimeoutException e) {
+            Slog.e(TAG, "Cannot create permission backup for " + user, e);
+            return null;
+        }
+    }
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-appliable state will be delayed and can be
+     * applied via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+        synchronized (mLock) {
+            mHasNoDelayedPermBackup.delete(user.getIdentifier());
+            mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user);
+        }
+    }
+
+    /**
+     * Try to apply permission backup that was previously not applied.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    private void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        synchronized (mLock) {
+            if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) {
+                return;
+            }
+
+            mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user,
+                    mContext.getMainExecutor(), (hasMoreBackup) -> {
+                        if (hasMoreBackup) {
+                            return;
+                        }
+
+                        synchronized (mLock) {
+                            mHasNoDelayedPermBackup.put(user.getIdentifier(), true);
+                        }
+                    });
+        }
+    }
+
+    /**
      * Returns {@code true} if the permission can be implied from another granted permission.
      * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
      * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
@@ -741,7 +836,6 @@
         if (ps == null) {
             return;
         }
-        final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg);
 
         final PermissionsState permissionsState = ps.getPermissionsState();
         PermissionsState origPermissions = permissionsState;
@@ -828,17 +922,9 @@
                     // For all apps normal permissions are install time ones.
                     grant = GRANT_INSTALL;
                 } else if (bp.isRuntime()) {
-                    // If a permission review is required for legacy apps we represent
-                    // their permissions as always granted runtime ones since we need
-                    // to keep the review required permission flag per user while an
-                    // install permission's state is shared across all users.
                     if (origPermissions.hasInstallPermission(bp.getName())) {
-                        // For legacy apps that became modern, install becomes runtime.
-                        grant = GRANT_UPGRADE;
-                    } else if (isLegacySystemApp) {
-                        // For legacy system apps, install becomes runtime.
-                        // We cannot check hasInstallPermission() for system apps since those
-                        // permissions were granted implicitly and not persisted pre-M.
+                        // Before Q we represented some runtime permissions as install permissions,
+                        // in Q we cannot do this anymore. Hence upgrade them all.
                         grant = GRANT_UPGRADE;
                     } else {
                         // For modern apps keep runtime permissions unchanged.
@@ -891,110 +977,111 @@
                             }
                             // Grant an install permission.
                             if (permissionsState.grantInstallPermission(bp) !=
-                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    PERMISSION_OPERATION_FAILURE) {
                                 changedInstallPermission = true;
                             }
                         } break;
 
                         case GRANT_RUNTIME: {
-                            // Grant previously granted runtime permissions.
-                            for (int userId : UserManagerService.getInstance().getUserIds()) {
-                                final PermissionState permissionState = origPermissions
+                            for (int userId : currentUserIds) {
+                                PermissionState permState = origPermissions
                                         .getRuntimePermissionState(perm, userId);
-                                int flags = permissionState != null
-                                        ? permissionState.getFlags() : 0;
-                                if (origPermissions.hasRuntimePermission(perm, userId)) {
-                                    // Don't propagate the permission in a permission review
-                                    // mode if the former was revoked, i.e. marked to not
-                                    // propagate on upgrade. Note that in a permission review
-                                    // mode install permissions are represented as constantly
-                                    // granted runtime ones since we need to keep a per user
-                                    // state associated with the permission. Also the revoke
-                                    // on upgrade flag is no longer applicable and is reset.
-                                    final boolean revokeOnUpgrade = (flags & PackageManager
-                                            .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-                                    if (revokeOnUpgrade) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                int flags = permState != null ? permState.getFlags() : 0;
+
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
-                                    if (!revokeOnUpgrade) {
-                                        if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                            // If we cannot put the permission as it was,
-                                            // we have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permState != null && permState.isGranted()) {
+                                            if (permissionsState.grantRuntimePermission(bp, userId)
+                                                    == PERMISSION_OPERATION_FAILURE) {
+                                                wasChanged = true;
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    if (permState == null) {
+                                        // New permission
+                                        if (PLATFORM_PACKAGE_NAME.equals(
+                                                bp.getSourcePackageName())) {
+                                            if (!bp.isRemoved()) {
+                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED
+                                                        | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                                wasChanged = true;
+                                            }
                                         }
                                     }
 
-                                    // If the app supports runtime permissions no need for a review.
-                                    if (appSupportsRuntimePermissions
-                                            && (flags & PackageManager
-                                                    .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
-                                    }
-                                } else if (!appSupportsRuntimePermissions) {
-                                    // For legacy apps that need a permission review, every new
-                                    // runtime permission is granted but it is pending a review.
-                                    // We also need to review only platform defined runtime
-                                    // permissions as these are the only ones the platform knows
-                                    // how to disable the API to simulate revocation as legacy
-                                    // apps don't expect to run with revoked permissions.
-                                    if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
-                                        if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
-                                                && !bp.isRemoved()) {
-                                            flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
-                                            // We changed the flags, hence have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
-                                        }
-                                    }
                                     if (permissionsState.grantRuntimePermission(bp, userId)
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // We changed the permission, hence have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            != PERMISSION_OPERATION_FAILURE) {
+                                        wasChanged = true;
                                     }
                                 }
-                                // Propagate the permission flags.
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
                                 permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
                         case GRANT_UPGRADE: {
-                            // Grant runtime permissions for a previously held install permission.
-                            final PermissionState permissionState = origPermissions
+                            // Upgrade from Pre-Q to Q permission model. Make all permissions
+                            // runtime
+                            PermissionState permState = origPermissions
                                     .getInstallPermissionState(perm);
-                            final int flags =
-                                    (permissionState != null) ? permissionState.getFlags() : 0;
+                            int flags = (permState != null) ? permState.getFlags() : 0;
 
+                            // Remove install permission
                             if (origPermissions.revokeInstallPermission(bp)
-                                    != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                // We will be transferring the permission flags, so clear them.
+                                    != PERMISSION_OPERATION_FAILURE) {
                                 origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                         PackageManager.MASK_PERMISSION_FLAGS, 0);
                                 changedInstallPermission = true;
                             }
 
-                            // If the permission is not to be promoted to runtime we ignore it and
-                            // also its other flags as they are not applicable to install permissions.
-                            if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
-                                for (int userId : currentUserIds) {
+                            for (int userId : currentUserIds) {
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
+                                    }
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                                PERMISSION_OPERATION_FAILURE) {
+                                             wasChanged = true;
+                                        }
+                                    }
+                                } else {
                                     if (permissionsState.grantRuntimePermission(bp, userId) !=
-                                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // Transfer the permission flags.
-                                        permissionsState.updatePermissionFlags(bp, userId,
-                                                flags, flags);
-                                        // If we granted the permission, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            PERMISSION_OPERATION_FAILURE) {
+                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
                                 }
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
+                                permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
@@ -1011,7 +1098,7 @@
                     }
                 } else {
                     if (permissionsState.revokeInstallPermission(bp) !=
-                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                            PERMISSION_OPERATION_FAILURE) {
                         // Also drop the permission flags.
                         permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                 PackageManager.MASK_PERMISSION_FLAGS, 0);
@@ -1095,6 +1182,8 @@
         AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
 
         String pkgName = pkg.packageName;
+        boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+                >= Build.VERSION_CODES.M;
 
         int[] users = UserManagerService.getInstance().getUserIds();
         int numUsers = users.length;
@@ -1119,27 +1208,17 @@
                             if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
                                     | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
                                     == 0) {
-                                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                                    if (permissionToOpCode(permission) != OP_NONE) {
-                                        setAppOpMode(permission, pkg, userId, MODE_IGNORED);
-
+                                if (supportsRuntimePermissions) {
+                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
+                                    if (revokeResult != PERMISSION_OPERATION_FAILURE) {
                                         if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking app-op "
-                                                    + permissionToOp(permission) + " for " + pkgName
+                                            Slog.i(TAG, "Revoking runtime permission "
+                                                    + permission + " for " + pkgName
                                                     + " as it is now requested");
                                         }
                                     }
                                 } else {
-                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
-                                    if (revokeResult
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking runtime permission " + permission
-                                                    + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
-                                    }
+                                    setAppOpMode(permission, pkg, userId, MODE_IGNORED);
                                 }
 
                                 List<String> fgPerms = mBackgroundPermissions.get(permission);
@@ -1925,7 +2004,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.grantInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionGranted();
                 }
@@ -1945,7 +2024,7 @@
 
         final int result = permissionsState.grantRuntimePermission(bp, userId);
         switch (result) {
-            case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+            case PERMISSION_OPERATION_FAILURE: {
                 return;
             }
 
@@ -2045,7 +2124,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.revokeInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionRevoked();
                 }
@@ -2054,7 +2133,7 @@
         }
 
         if (permissionsState.revokeRuntimePermission(bp, userId) ==
-                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                PERMISSION_OPERATION_FAILURE) {
             return;
         }
 
@@ -2522,6 +2601,8 @@
             throw new IllegalStateException("Signature|privileged permissions not in "
                     + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
         }
+
+        mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
     }
 
     private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
@@ -2574,7 +2655,7 @@
         return mBackgroundPermissions;
     }
 
-    private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+    private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
         @Override
         public void systemReady() {
             PermissionManagerService.this.systemReady();
@@ -2737,5 +2818,21 @@
                 return mSettings.getPermissionLocked(permName);
             }
         }
+
+        @Override
+        public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+            return PermissionManagerService.this.backupRuntimePermissions(user);
+        }
+
+        @Override
+        public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreRuntimePermissions(backup, user);
+        }
+
+        @Override
+        public void restoreDelayedRuntimePermissions(@NonNull String packageName,
+                @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
similarity index 96%
rename from services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
rename to services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index f4979746..1dd2408 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -18,19 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.permission.PermissionManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 /**
- * Internal interfaces to be used by other components within the system server.
+ * Internal interfaces services.
+ *
+ * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
  */
-public abstract class PermissionManagerInternal {
+public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
     /**
      * Callbacks invoked when interesting actions have been taken on a permission.
      * <p>
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index 0892b32..2280d3f 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -16,6 +16,9 @@
                 },
                 {
                     "include-filter": "android.permission.cts.SplitPermissionTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.PermissionFlagsTest"
                 }
             ]
         },
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index fdcafa7..0c6b773 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -42,10 +42,12 @@
     private final WindowManagerInternal mWindowManagerInternal;
     private final DisplayManagerInternal mDisplayManagerInternal;
     private final int mDisplayId;
+    private final Handler mHandler;
 
     /** The display area while device is folded. */
     private final Rect mFoldedArea;
-    private final Handler mHandler;
+    /** The display area to override the original folded area. */
+    private Rect mOverrideFoldedArea = new Rect();
 
     private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo();
     private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>();
@@ -70,14 +72,23 @@
             return;
         }
         if (folded) {
-            mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo);
-            final int dx = (mNonOverrideDisplayInfo.logicalWidth - mFoldedArea.width()) / 2
-                    - mFoldedArea.left;
-            final int dy = (mNonOverrideDisplayInfo.logicalHeight - mFoldedArea.height()) / 2
-                    - mFoldedArea.top;
+            Rect foldedArea;
+            if (!mOverrideFoldedArea.isEmpty()) {
+                foldedArea = mOverrideFoldedArea;
+            } else if (!mFoldedArea.isEmpty()) {
+                foldedArea = mFoldedArea;
+            } else {
+                return;
+            }
 
-            mWindowManagerInternal.setForcedDisplaySize(mDisplayId, mFoldedArea.width(),
-                    mFoldedArea.height());
+            mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo);
+            final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2
+                    - foldedArea.left;
+            final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2
+                    - foldedArea.top;
+
+            mWindowManagerInternal.setForcedDisplaySize(mDisplayId,
+                    foldedArea.width(), foldedArea.height());
             mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy);
         } else {
             mWindowManagerInternal.clearForcedDisplaySize(mDisplayId);
@@ -114,6 +125,18 @@
         mListeners.unregister(listener);
     }
 
+    void setOverrideFoldedArea(Rect area) {
+        mOverrideFoldedArea.set(area);
+    }
+
+    Rect getFoldedArea() {
+        if (!mOverrideFoldedArea.isEmpty()) {
+            return mOverrideFoldedArea;
+        } else {
+            return mFoldedArea;
+        }
+    }
+
     /**
      * Only used for the case that persist.debug.force_foldable is set.
      * This is using proximity sensor to simulate the fold state switch.
@@ -125,7 +148,7 @@
             return null;
         }
 
-        final DisplayFoldController result = create(displayId);
+        final DisplayFoldController result = create(context, displayId);
         sensorManager.registerListener(new SensorEventListener() {
             @Override
             public void onSensorChanged(SensorEvent event) {
@@ -141,13 +164,17 @@
         return result;
     }
 
-    static DisplayFoldController create(int displayId) {
+    static DisplayFoldController create(Context context, int displayId) {
         final DisplayManagerInternal displayService =
                 LocalServices.getService(DisplayManagerInternal.class);
-        final DisplayInfo displayInfo = new DisplayInfo();
-        displayService.getNonOverrideDisplayInfo(displayId, displayInfo);
-        final Rect foldedArea = new Rect(0, displayInfo.logicalHeight / 2,
-                displayInfo.logicalWidth, displayInfo.logicalHeight);
+        final String configFoldedArea = context.getResources().getString(
+                com.android.internal.R.string.config_foldedArea);
+        final Rect foldedArea;
+        if (configFoldedArea == null || configFoldedArea.isEmpty()) {
+            foldedArea = new Rect();
+        } else {
+            foldedArea = Rect.unflattenFromString(configFoldedArea);
+        }
 
         return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class),
                 displayService, displayId, foldedArea, DisplayThread.getHandler());
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b00193f..c87a81d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -22,7 +22,6 @@
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.Context.CONTEXT_RESTRICTED;
-import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
 import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -36,6 +35,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.STATE_OFF;
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
@@ -84,10 +84,8 @@
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
-        .CAMERA_LENS_COVER_ABSENT;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
-        .CAMERA_LENS_UNCOVERED;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
@@ -127,6 +125,7 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.hdmi.HdmiAudioSystemClient;
@@ -371,6 +370,7 @@
     IStatusBarService mStatusBarService;
     StatusBarManagerInternal mStatusBarManagerInternal;
     AudioManagerInternal mAudioManagerInternal;
+    DisplayManager mDisplayManager;
     boolean mPreloadedRecentApps;
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -1717,7 +1717,8 @@
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
         mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
@@ -1858,7 +1859,7 @@
         readConfigurationDependentBehaviors();
 
         if (mLidControlsDisplayFold) {
-            mDisplayFoldController = DisplayFoldController.create(DEFAULT_DISPLAY);
+            mDisplayFoldController = DisplayFoldController.create(context, DEFAULT_DISPLAY);
         } else if (SystemProperties.getBoolean("persist.debug.force_foldable", false)) {
             mDisplayFoldController = DisplayFoldController.createWithProxSensor(context,
                     DEFAULT_DISPLAY);
@@ -2508,8 +2509,7 @@
             return context;
         }
 
-        final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE);
-        final Display targetDisplay = dm.getDisplay(displayId);
+        final Display targetDisplay = mDisplayManager.getDisplay(displayId);
         if (targetDisplay == null) {
             // Failed to obtain the non-default display where splash screen should be shown,
             // lets not show at all.
@@ -3222,6 +3222,21 @@
     }
 
     @Override
+    public void setOverrideFoldedArea(Rect area) {
+        if (mDisplayFoldController != null) {
+            mDisplayFoldController.setOverrideFoldedArea(area);
+        }
+    }
+
+    @Override
+    public Rect getFoldedArea() {
+        if (mDisplayFoldController != null) {
+            return mDisplayFoldController.getFoldedArea();
+        }
+        return new Rect();
+    }
+
+    @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
             throws RemoteException {
         synchronized (mLock) {
@@ -3655,7 +3670,7 @@
                 // Reset the pending key
                 mPendingWakeKey = PENDING_KEY_NULL;
             }
-        } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) {
+        } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
             // If we're currently dozing with the screen on and the keyguard showing, pass the key
             // to the application but preserve its wake key status to make sure we still move
             // from dozing to fully interactive if we would normally go from off to fully
@@ -4126,7 +4141,8 @@
     // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
+            int policyFlags) {
         if ((policyFlags & FLAG_WAKE) != 0) {
             if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
                     PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
@@ -4134,7 +4150,7 @@
             }
         }
 
-        if (shouldDispatchInputWhenNonInteractive(null)) {
+        if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
             return ACTION_PASS_TO_USER;
         }
 
@@ -4149,9 +4165,15 @@
         return 0;
     }
 
-    private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) {
-        final boolean displayOff = (mDefaultDisplay == null
-                || mDefaultDisplay.getState() == STATE_OFF);
+    private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
+        // Apply the default display policy to unknown displays as well.
+        final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
+                || displayId == INVALID_DISPLAY;
+        final Display display = isDefaultDisplay
+                ? mDefaultDisplay
+                : mDisplayManager.getDisplay(displayId);
+        final boolean displayOff = (display == null
+                || display.getState() == STATE_OFF);
 
         if (displayOff && !mHasFeatureWatch) {
             return false;
@@ -4163,25 +4185,25 @@
         }
 
         // Watches handle BACK specially
-        if (mHasFeatureWatch
-                && event != null
-                && (event.getKeyCode() == KeyEvent.KEYCODE_BACK
-                        || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) {
+        if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK
+                || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY)) {
             return false;
         }
 
-        // Send events to a dozing dream even if the screen is off since the dream
-        // is in control of the state of the screen.
-        IDreamManager dreamManager = getDreamManager();
+        // TODO(b/123372519): Refine when dream can support multi display.
+        if (isDefaultDisplay) {
+            // Send events to a dozing dream even if the screen is off since the dream
+            // is in control of the state of the screen.
+            IDreamManager dreamManager = getDreamManager();
 
-        try {
-            if (dreamManager != null && dreamManager.isDreaming()) {
-                return true;
+            try {
+                if (dreamManager != null && dreamManager.isDreaming()) {
+                    return true;
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "RemoteException when checking if dreaming", e);
             }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "RemoteException when checking if dreaming", e);
         }
-
         // Otherwise, consume events since the user can't see what is being
         // interacted with.
         return false;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index d1bd102..d7e4b6c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -64,6 +64,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.Context;
@@ -1003,11 +1004,13 @@
      * affect the power state of the device, for example, waking on motions.
      * Generally, it's best to keep as little as possible in the queue thread
      * because it's the most fragile.
+     * @param displayId The display ID of the motion event.
      * @param policyFlags The policy flags associated with the motion.
      *
      * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}.
      */
-    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
+    int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
+            int policyFlags);
 
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
@@ -1468,6 +1471,20 @@
     default void unregisterDisplayFoldListener(IDisplayFoldListener listener) {}
 
     /**
+     * Overrides the folded area.
+     *
+     * @param area the overriding folded area or an empty {@code Rect} to clear the override.
+     */
+    default void setOverrideFoldedArea(@NonNull Rect area) {}
+
+    /**
+     * Get the display folded area.
+     */
+    default @NonNull Rect getFoldedArea() {
+        return new Rect();
+    }
+
+    /**
      * Updates the flag about whether AOD is showing.
      *
      * @return whether the value was changed.
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 3534cf3..888dd99 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -17,10 +17,13 @@
 package com.android.server.policy.role;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
 import android.os.Debug;
 import android.provider.Settings;
 import android.telecom.TelecomManager;
@@ -33,6 +36,7 @@
 import com.android.server.LocalServices;
 import com.android.server.role.RoleManagerService;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -54,19 +58,44 @@
     @NonNull
     private final Context mContext;
 
-    public LegacyRoleResolutionPolicy(Context context) {
+    public LegacyRoleResolutionPolicy(@NonNull Context context) {
         mContext = context;
     }
 
+    @NonNull
     @Override
-    public List<String> getRoleHolders(String roleName, int userId) {
+    public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) {
         switch (roleName) {
+            case RoleManager.ROLE_ASSISTANT: {
+                String legacyAssistant = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
+                if (legacyAssistant == null || legacyAssistant.isEmpty()) {
+                    return Collections.emptyList();
+                } else {
+                    return Collections.singletonList(
+                            ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+                }
+            }
+            case RoleManager.ROLE_BROWSER: {
+                PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                        PackageManagerInternal.class);
+                String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
+                        userId);
+                return CollectionUtils.singletonOrEmpty(packageName);
+            }
+            case RoleManager.ROLE_DIALER: {
+                String setting = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
+                return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
+                        ? setting
+                        : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
+            }
             case RoleManager.ROLE_SMS: {
                 // Moved over from SmsApplication#getApplication
                 String result = Settings.Secure.getStringForUser(
                         mContext.getContentResolver(),
                         Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-
                 // TODO: STOPSHIP: Remove the following code once we read the value of
                 //  config_defaultSms in RoleControllerService.
                 if (result == null) {
@@ -92,34 +121,13 @@
                     SmsApplication.SmsApplicationData app = applicationData;
                     result = app == null ? null : app.mPackageName;
                 }
-
                 return CollectionUtils.singletonOrEmpty(result);
             }
-            case RoleManager.ROLE_ASSISTANT: {
-                String legacyAssistant = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
-
-                if (legacyAssistant == null || legacyAssistant.isEmpty()) {
-                    return Collections.emptyList();
-                } else {
-                    return Collections.singletonList(
-                            ComponentName.unflattenFromString(legacyAssistant).getPackageName());
-                }
-            }
-            case RoleManager.ROLE_DIALER: {
-                String setting = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
-
-                return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
-                        ? setting
-                        : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
-            }
-            case RoleManager.ROLE_BROWSER: {
-                PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                        PackageManagerInternal.class);
-                String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
-                        userId);
+            case RoleManager.ROLE_HOME: {
+                PackageManager packageManager = mContext.getPackageManager();
+                List<ResolveInfo> resolveInfos = new ArrayList<>();
+                ComponentName componentName = packageManager.getHomeActivities(resolveInfos);
+                String packageName = componentName != null ? componentName.getPackageName() : null;
                 return CollectionUtils.singletonOrEmpty(packageName);
             }
             default: {
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 4186154..8740256 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -123,6 +123,9 @@
     public AttentionDetector(Runnable onUserAttention, Object lock) {
         mOnUserAttention = onUserAttention;
         mLock = lock;
+
+        // Device starts with an awake state upon boot.
+        mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
     }
 
     public void systemReady(Context context) {
@@ -145,7 +148,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
             }
-            return nextScreenDimming;
+            return whenToCheck;
         } else if (whenToStopExtending < whenToCheck) {
             if (DEBUG) {
                 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1782b6a..176dbbf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -538,6 +538,9 @@
     // True if we are currently in VR Mode.
     private boolean mIsVrModeEnabled;
 
+    // True if we in the process of performing a forceSuspend
+    private boolean mForceSuspendActive;
+
     private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
         @Override
         public void onUserSwitching(int newUserId) throws RemoteException {}
@@ -684,6 +687,11 @@
         public void nativeSetFeature(int featureId, int data) {
             PowerManagerService.nativeSetFeature(featureId, data);
         }
+
+        /** Wrapper for PowerManager.nativeForceSuspend */
+        public boolean nativeForceSuspend() {
+            return PowerManagerService.nativeForceSuspend();
+        }
     }
 
     @VisibleForTesting
@@ -718,6 +726,7 @@
     private static native void nativeSetAutoSuspend(boolean enable);
     private static native void nativeSendPowerHint(int hintId, int data);
     private static native void nativeSetFeature(int featureId, int data);
+    private static native boolean nativeForceSuspend();
 
     public PowerManagerService(Context context) {
         this(context, new Injector());
@@ -1427,7 +1436,7 @@
         }
 
         if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
-                || !mBootCompleted || !mSystemReady) {
+                || !mBootCompleted || !mSystemReady || mForceSuspendActive) {
             return false;
         }
 
@@ -1463,8 +1472,13 @@
         }
     }
 
-    // This method is called goToSleep for historical reasons but we actually start
-    // dozing before really going to sleep.
+    /**
+     * Puts the system in doze.
+     *
+     * This method is called goToSleep for historical reasons but actually attempts to DOZE,
+     * and only tucks itself in to SLEEP if requested with the flag
+     * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}.
+     */
     @SuppressWarnings("deprecation")
     private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
         if (DEBUG_SPEW) {
@@ -1481,35 +1495,10 @@
 
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
         try {
-            switch (reason) {
-                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
-                    Slog.i(TAG, "Going to sleep due to device administration policy "
-                            + "(uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
-                    Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
-                    Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
-                    Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
-                    Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_HDMI:
-                    Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
-                    break;
-                case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY:
-                    Slog.i(TAG, "Going to sleep by an accessibility service request (uid "
-                            + uid +")...");
-                    break;
-                default:
-                    Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
-                    reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
-                    break;
-            }
+            reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
+                    Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
+            Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
+                    + " (uid " + uid + ")...");
 
             mLastSleepTime = eventTime;
             mLastSleepReason = reason;
@@ -3063,10 +3052,10 @@
             if (appid >= Process.FIRST_APPLICATION_UID) {
                 // Cached inactive processes are never allowed to hold wake locks.
                 if (mConstants.NO_CACHED_WAKE_LOCKS) {
-                    disabled = !wakeLock.mUidState.mActive &&
-                            wakeLock.mUidState.mProcState
+                    disabled = mForceSuspendActive
+                            || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState
                                     != ActivityManager.PROCESS_STATE_NONEXISTENT &&
-                            wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER;
+                            wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER);
                 }
                 if (mDeviceIdleMode) {
                     // If we are in idle mode, we will also ignore all partial wake locks that are
@@ -3241,6 +3230,34 @@
         }
     }
 
+    private boolean forceSuspendInternal(int uid) {
+        try {
+            synchronized (mLock) {
+                mForceSuspendActive = true;
+                // Place the system in an non-interactive state
+                goToSleepInternal(SystemClock.uptimeMillis(),
+                        PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
+
+                // Disable all the partial wake locks as well
+                updateWakeLockDisabledStatesLocked();
+            }
+
+            Slog.i(TAG, "Force-Suspending (uid " + uid + ")...");
+            boolean success = mNativeWrapper.nativeForceSuspend();
+            if (!success) {
+                Slog.i(TAG, "Force-Suspending failed in native.");
+            }
+            return success;
+        } finally {
+            synchronized (mLock) {
+                mForceSuspendActive = false;
+                // Re-enable wake locks once again.
+                updateWakeLockDisabledStatesLocked();
+            }
+        }
+    }
+
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -4743,6 +4760,20 @@
             }
         }
 
+        @Override // binder call
+        public boolean forceSuspend() {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
+
+            final int uid = Binder.getCallingUid();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return forceSuspendInternal(uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         @Override // Binder call
         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/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 21bf9de..c145a22 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -111,7 +111,8 @@
     /** @see #getRoleHolders(String, int) */
     public interface RoleHoldersResolver {
         /** @return a list of packages that hold a given role for a given user */
-        List<String> getRoleHolders(String roleName, int userId);
+        @NonNull
+        List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId);
     }
 
     /**
@@ -154,6 +155,7 @@
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider());
+        packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider());
 
         registerUserRemovedReceiver();
     }
@@ -194,7 +196,7 @@
                 }
                 performInitialGrantsIfNecessary(userId);
             }
-        }, UserHandle.SYSTEM, intentFilter, null /* broadcastPermission */, null /* handler */);
+        }, UserHandle.ALL, intentFilter, null, null);
 
         getContext().getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED), false,
@@ -741,4 +743,33 @@
             }
         }
     }
+
+    private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider {
+
+        @Nullable
+        @Override
+        public String getDefaultHome(@UserIdInt int userId) {
+            return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
+                    RoleManager.ROLE_HOME));
+        }
+
+        @Override
+        public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) {
+            IRoleManagerCallback callback = new IRoleManagerCallback.Stub() {
+                @Override
+                public void onSuccess() {}
+                @Override
+                public void onFailure() {
+                    Slog.e(LOG_TAG, "Failed to set default home: " + packageName);
+                }
+            };
+            if (packageName != null) {
+                getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
+                        packageName, 0, callback);
+            } else {
+                getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
+                        callback);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 95c3f4c..05d3c17 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.rollback;
 
+import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -40,6 +41,7 @@
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.provider.DeviceConfig;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseBooleanArray;
@@ -61,6 +63,7 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Implementation of service that manages APK level rollbacks.
@@ -71,13 +74,19 @@
 
     // Rollbacks expire after 48 hours.
     // TODO: How to test rollback expiration works properly?
-    private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+    private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
+            TimeUnit.HOURS.toMillis(48);
 
     // Lock used to synchronize accesses to in-memory rollback data
     // structures. By convention, methods with the suffix "Locked" require
     // mLock is held when they are called.
     private final Object mLock = new Object();
 
+    // No need for guarding with lock because value is only accessed in handler thread
+    // and the value will be written on boot complete. Initialization here happens before
+    // handler threads are running so that's fine.
+    private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+
     // Used for generating rollback IDs.
     private final Random mRandom = new SecureRandom();
 
@@ -484,7 +493,25 @@
         });
     }
 
+    private void updateRollbackLifetimeDurationInMillis() {
+        String strRollbackLifetimeInMillis = DeviceConfig.getProperty(
+                DeviceConfig.Rollback.BOOT_NAMESPACE,
+                DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS);
+
+        try {
+            mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null)
+                    ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS
+                    : Long.parseLong(strRollbackLifetimeInMillis);
+        } catch (NumberFormatException e) {
+            mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+        }
+    }
+
     void onBootCompleted() {
+        getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
+        // Also posts to handler thread
+        scheduleExpiration(0);
+
         getHandler().post(() -> {
             // Check to see if any staged sessions with rollback enabled have
             // been applied.
@@ -565,8 +592,6 @@
         for (RollbackInfo info : mRecentlyExecutedRollbacks) {
             mAllocatedRollbackIds.put(info.getRollbackId(), true);
         }
-
-        scheduleExpiration(0);
     }
 
     /**
@@ -700,8 +725,7 @@
                 if (!data.isAvailable) {
                     continue;
                 }
-
-                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+                if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
                     iter.remove();
                     deleteRollback(data);
                 } else if (oldest == null || oldest.isAfter(data.timestamp)) {
@@ -711,7 +735,7 @@
         }
 
         if (oldest != null) {
-            scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+            scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
                         ChronoUnit.MILLIS));
         }
     }
@@ -819,6 +843,7 @@
             String packageName = newPackage.packageName;
             for (PackageRollbackInfo info : rd.packages) {
                 if (info.getPackageName().equals(packageName)) {
+                    info.getInstalledUsers().addAll(IntArray.wrap(installedUsers));
                     AppDataRollbackHelper.SnapshotAppDataResult rs =
                             mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers);
                     info.getPendingBackups().addAll(rs.pendingBackups);
@@ -851,7 +876,7 @@
      * the child sessions, not the parent session.
      */
     private boolean enableRollbackForSession(PackageInstaller.SessionInfo session,
-            int[] installedUsers, boolean snapshotUserData) {
+            @NonNull int[] installedUsers, boolean snapshotUserData) {
         // TODO: Don't attempt to enable rollback for split installs.
         final int installFlags = session.installFlags;
         if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
@@ -993,7 +1018,7 @@
             }
 
             if (!session.isMultiPackage()) {
-                if (!enableRollbackForSession(session, null, false)) {
+                if (!enableRollbackForSession(session, new int[0], false)) {
                     Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
                     result.offer(false);
                     return;
@@ -1007,7 +1032,7 @@
                         result.offer(false);
                         return;
                     }
-                    if (!enableRollbackForSession(childSession, null, false)) {
+                    if (!enableRollbackForSession(childSession, new int[0], false)) {
                         Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
                         result.offer(false);
                         return;
@@ -1144,8 +1169,8 @@
                         packages.add(data.packages.get(i).getPackageName());
                     }
                     mPackageHealthObserver.startObservingHealth(packages,
-                            ROLLBACK_LIFETIME_DURATION_MILLIS);
-                    scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
+                            mRollbackLifetimeDurationInMillis);
+                    scheduleExpiration(mRollbackLifetimeDurationInMillis);
                 } catch (IOException e) {
                     Log.e(TAG, "Unable to enable rollback", e);
                     deleteRollback(data);
diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING
index c1d95ac..6be93a0 100644
--- a/services/core/java/com/android/server/rollback/TEST_MAPPING
+++ b/services/core/java/com/android/server/rollback/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "RollbackTest"
+    },
+    {
+      "name": "StagedRollbackTest"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index dd2cda2..4815e5c 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -85,7 +85,9 @@
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
@@ -167,6 +169,7 @@
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
+    public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
     /**
      * The last report time is provided with each intent registered to
      * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
@@ -356,6 +359,22 @@
     }
 
     @Override
+    public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) {
+        enforceCallingPermission();
+        IntentSender intentSender = new IntentSender(intentSenderBinder);
+        Intent intent = new Intent();
+        intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
+        try {
+            intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
+            if (DEBUG) {
+                Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
+            }
+        } catch (IntentSender.SendIntentException e) {
+            Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender");
+        }
+    }
+
+    @Override
     public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
             long subscriptionId, long subscriptionRuleId, String[] cookies,
             StatsDimensionsValue dimensionsValue) {
@@ -1169,7 +1188,7 @@
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
-            return;
+            throw new IllegalStateException("binderStats is null");
         }
 
         List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
@@ -1200,7 +1219,7 @@
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
-            return;
+            throw new IllegalStateException("binderStats is null");
         }
 
         ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
@@ -1218,7 +1237,7 @@
             List<StatsLogEventWrapper> pulledData) {
         LooperStats looperStats = LocalServices.getService(LooperStats.class);
         if (looperStats == null) {
-            return;
+            throw new IllegalStateException("looperStats null");
         }
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
@@ -1689,18 +1708,19 @@
     private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         if (this.mKernelCpuThreadReader == null) {
-            return;
+            throw new IllegalStateException("mKernelCpuThreadReader is null");
         }
         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
                 this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
         if (processCpuUsages == null) {
-            return;
+            throw new IllegalStateException("processCpuUsages is null");
         }
         int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
         if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) {
-            Slog.w(TAG, "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
-                    + " frequencies, but got " + cpuFrequencies.length);
-            return;
+            String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
+                    + " frequencies, but got " + cpuFrequencies.length;
+            Slog.w(TAG, message);
+            throw new IllegalStateException(message);
         }
         for (int i = 0; i < processCpuUsages.size(); i++) {
             KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
@@ -1709,10 +1729,11 @@
             for (int j = 0; j < threadCpuUsages.size(); j++) {
                 KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
                 if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
-                    Slog.w(TAG, "Unexpected number of usage times,"
+                    String message = "Unexpected number of usage times,"
                             + " expected " + cpuFrequencies.length
-                            + " but got " + threadCpuUsage.usageTimesMillis.length);
-                    continue;
+                            + " but got " + threadCpuUsage.usageTimesMillis.length;
+                    Slog.w(TAG, message);
+                    throw new IllegalStateException(message);
                 }
 
                 StatsLogEventWrapper e =
@@ -1923,6 +1944,41 @@
         }
     }
 
+    private void pullTimeZoneDataInfo(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        String tzDbVersion = "Unknown";
+        try {
+            tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
+        } catch (Exception e) {
+            Log.e(TAG, "Getting tzdb version failed: ", e);
+        }
+
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(tzDbVersion);
+        pulledData.add(e);
+    }
+
+    private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+        if (storageManager != null) {
+            List<VolumeInfo> volumes = storageManager.getVolumes();
+            for (VolumeInfo vol : volumes) {
+                final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
+                final DiskInfo diskInfo = vol.getDisk();
+                if (diskInfo != null && diskInfo.isSd()) {
+                    if (envState.equals(Environment.MEDIA_MOUNTED)) {
+                        StatsLogEventWrapper e =
+                                new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                        e.writeInt(vol.getType() + 1);
+                        e.writeLong(diskInfo.size);
+                        pulledData.add(e);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -2111,6 +2167,14 @@
                 pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.TIME_ZONE_DATA_INFO: {
+                pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.SDCARD_INFO: {
+                pullSDCardInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index ff01d46..b121298 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -19,6 +19,7 @@
 import static android.app.StatusBarManager.DISABLE2_NONE;
 import static android.app.StatusBarManager.DISABLE_NONE;
 
+import android.app.StatusBarManager.DisableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
@@ -26,6 +27,7 @@
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.service.quicksettings.TileService;
+import android.util.Pair;
 
 import java.io.PrintWriter;
 
@@ -68,6 +70,8 @@
                     return runGetStatusIcons();
                 case "disable-for-setup":
                     return runDisableForSetup();
+                case "send-disable-flag":
+                    return runSendDisableFlag();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -132,6 +136,47 @@
         return 0;
     }
 
+    private int runSendDisableFlag() {
+        String pkg = mContext.getPackageName();
+        int disable1 = DISABLE_NONE;
+        int disable2 = DISABLE2_NONE;
+
+        DisableInfo info = new DisableInfo();
+
+        String arg = getNextArg();
+        while (arg != null) {
+            switch (arg) {
+                case "search":
+                    info.setSearchDisabled(true);
+                    break;
+                case "home":
+                    info.setNagivationHomeDisabled(true);
+                    break;
+                case "recents":
+                    info.setRecentsDisabled(true);
+                    break;
+                case "notification-alerts":
+                    info.setNotificationPeekingDisabled(true);
+                    break;
+                case "statusbar-expansion":
+                    info.setStatusBarExpansionDisabled(true);
+                    break;
+
+                default:
+                    break;
+            }
+
+            arg = getNextArg();
+        }
+
+        Pair<Integer, Integer> flagPair = info.toFlags();
+
+        mInterface.disable(flagPair.first, sToken, pkg);
+        mInterface.disable2(flagPair.second, sToken, pkg);
+
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -166,6 +211,17 @@
         pw.println("  disable-for-setup DISABLE");
         pw.println("    If true, disable status bar components unsuitable for device setup");
         pw.println("");
+        pw.println("  send-disable-flag FLAG...");
+        pw.println("    Send zero or more disable flags (parsed individually) to StatusBarManager");
+        pw.println("    Valid options:");
+        pw.println("        <blank>             - equivalent to \"none\"");
+        pw.println("        none                - re-enables all components");
+        pw.println("        search              - disable search");
+        pw.println("        home                - disable naviagation home");
+        pw.println("        recents             - disable recents/overview");
+        pw.println("        notification-peek   - disable notification peeking");
+        pw.println("        statusbar-expansion - disable status bar expansion");
+        pw.println("");
     }
 
     /**
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 7a3f030..f581bc0 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -44,7 +44,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 /**
  * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -133,7 +133,7 @@
     }
 
     private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
-        return LocalServices.getService(PermissionManagerInternal.class)
+        return LocalServices.getService(PermissionManagerServiceInternal.class)
                 .getDefaultPermissionGrantPolicy();
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 423ec4c..c7044a1 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -366,17 +366,22 @@
         } catch (RemoteException e) {
         }
 
-        if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
-            trusted = trusted && (!showingKeyguard || isFromUnlock) && userId == mCurrentUser;
-            if (DEBUG) {
-                Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
-                        + " && " + Boolean.toString(!showingKeyguard)
-                        + " && " + Boolean.toString(userId == mCurrentUser));
-            }
-        }
-
         boolean changed;
         synchronized (mUserIsTrusted) {
+            if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
+                // In extend unlock trust agents can only set the device to trusted if it already
+                // trusted or the device is unlocked. Attempting to set the device as trusted
+                // when the device is locked will be ignored.
+                changed = mUserIsTrusted.get(userId) != trusted;
+                trusted = trusted
+                        && (!showingKeyguard || isFromUnlock || !changed)
+                        && userId == mCurrentUser;
+                if (DEBUG) {
+                    Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
+                            + " && " + Boolean.toString(!showingKeyguard)
+                            + " && " + Boolean.toString(userId == mCurrentUser));
+                }
+            }
             changed = mUserIsTrusted.get(userId) != trusted;
             mUserIsTrusted.put(userId, trusted);
         }
diff --git a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
new file mode 100644
index 0000000..f26121e
--- /dev/null
+++ b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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.utils;
+
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+
+import com.android.server.RescueParty;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utilities for interacting with the {@link android.provider.DeviceConfig}.
+ *
+ * @hide
+ */
+public final class FlagNamespaceUtils {
+    /**
+     * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that
+     * Settings were reset by the RescueParty, no actual namespace with this name exists in
+     * {@link DeviceConfig}.
+     */
+    public static final String NAMESPACE_NO_PACKAGE = "no_package";
+
+    /**
+     * Name of the special namespace in DeviceConfig table used for communicating resets.
+     */
+    private static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
+    /**
+     * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link
+     * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the
+     * first time flags are written to the new namespace in the {@link DeviceConfig}.
+     */
+    private static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
+    /**
+     * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter
+     * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently
+     * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given
+     * namespace flags are reset.
+     */
+    private static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
+    private static final String DELIMITER = ":";
+    /**
+     * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG}
+     * when communicating recently reset by the RescueParty namespace values.
+     */
+    private static final int MAX_COUNTER_VALUE = 50;
+
+    private static int sKnownResetNamespacesFlagCounter = -1;
+
+    /**
+     * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
+     * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace
+     * in the consumed namespacesList. These flags are used for communicating the namespaces
+     * (aka platform packages) whose flags in {@link DeviceConfig} were just reset
+     * by the RescueParty.
+     */
+    public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) {
+        if (namespacesList == null) {
+            return;
+        }
+        for (String namespace : namespacesList) {
+            addToKnownResetNamespaces(namespace);
+        }
+    }
+
+    /**
+     * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
+     * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace.
+     * This flag is used for communicating the namespace (aka platform package) whose flags
+     * in {@link DeviceConfig} were just reset by the RescueParty.
+     */
+    public static void addToKnownResetNamespaces(String namespace) {
+        int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter();
+        DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY,
+                RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter,
+                namespace, /*makeDefault=*/ true);
+    }
+
+    /**
+     * Reset all namespaces in DeviceConfig with consumed resetMode.
+     */
+    public static void resetDeviceConfig(int resetMode) {
+        List<String> allKnownNamespaces = getAllKnownDeviceConfigNamespacesList();
+        for (String namespace : allKnownNamespaces) {
+            DeviceConfig.resetToDefaults(resetMode, namespace);
+        }
+        addToKnownResetNamespaces(allKnownNamespaces);
+    }
+
+    /**
+     * Returns a list of all known DeviceConfig namespaces, except for the special {@link
+     * #NAMESPACE_RESCUE_PARTY}
+     */
+    private static List<String> getAllKnownDeviceConfigNamespacesList() {
+        String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY,
+                ALL_KNOWN_NAMESPACES_FLAG);
+        List<String> namespacesList = toStringList(namespacesStr);
+        namespacesList.remove(NAMESPACE_RESCUE_PARTY);
+        return namespacesList;
+    }
+
+    private static List<String> toStringList(String serialized) {
+        if (serialized == null || serialized.length() == 0) {
+            return new ArrayList<>();
+        }
+        return Arrays.asList(serialized.split(DELIMITER));
+    }
+
+    private static int incrementAndRetrieveResetNamespacesFlagCounter() {
+        sKnownResetNamespacesFlagCounter++;
+        if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) {
+            sKnownResetNamespacesFlagCounter = 0;
+        }
+        return sKnownResetNamespacesFlagCounter;
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0ef8a0..071dde7 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2243,12 +2243,9 @@
         synchronized (mLock) {
             mInAmbientMode = inAmbientMode;
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
-            final boolean hasConnection = data != null && data.connection != null;
-            final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
-
             // The wallpaper info is null for image wallpaper, also use the engine in this case.
-            if (hasConnection && (info == null && isAodImageWallpaperEnabled()
-                    || info != null && info.supportsAmbientMode())) {
+            if (data != null && data.connection != null && (data.connection.mInfo == null
+                    || data.connection.mInfo.supportsAmbientMode())) {
                 // TODO(multi-display) Extends this method with specific display.
                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0251efb..087de69 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -87,8 +87,6 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
 import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
@@ -2677,8 +2675,9 @@
      * Get the configuration orientation by the requested screen orientation
      * ({@link ActivityInfo.ScreenOrientation}) of this activity.
      *
-     * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
-     *         {@link #ORIENTATION_UNDEFINED}).
+     * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+     *         {@link Configuration#ORIENTATION_PORTRAIT},
+     *         {@link Configuration#ORIENTATION_UNDEFINED}).
      */
     int getRequestedConfigurationOrientation() {
         final int screenOrientation = getOrientation();
@@ -2827,28 +2826,6 @@
             outAppBounds.setEmpty();
         }
 
-        // TODO(b/112288258): Remove below calculation because the position information in bounds
-        // will be replaced by the offset of surface.
-        final Rect appBounds = parentConfig.windowConfiguration.getAppBounds();
-        if (appBounds != null) {
-            final Rect outBounds = inOutConfig.windowConfiguration.getBounds();
-            final int activityWidth = outBounds.width();
-            final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId());
-            if (navBarPosition == NAV_BAR_LEFT) {
-                // Position the activity frame on the opposite side of the nav bar.
-                outBounds.left = appBounds.right - activityWidth;
-                outBounds.right = appBounds.right;
-            } else if (navBarPosition == NAV_BAR_RIGHT) {
-                // Position the activity frame on the opposite side of the nav bar.
-                outBounds.left = 0;
-                outBounds.right = activityWidth + appBounds.left;
-            } else if (appBounds.width() > activityWidth) {
-                // Horizontally center the frame.
-                outBounds.left = appBounds.left + (appBounds.width() - activityWidth) / 2;
-                outBounds.right = outBounds.left + activityWidth;
-            }
-        }
-
         task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds);
     }
 
@@ -2936,14 +2913,36 @@
                 // should be given the aspect ratio.
                 activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < minAspectRatio && minAspectRatio != 0) {
-            if (containingAppWidth < containingAppHeight) {
-                // Width is the shorter side, so we use the height to figure-out what the max. width
-                // should be given the aspect ratio.
+        } else if (containingRatio < minAspectRatio) {
+            boolean adjustWidth;
+            switch (getRequestedConfigurationOrientation()) {
+                case ORIENTATION_LANDSCAPE:
+                    // Width should be the longer side for this landscape app, so we use the width
+                    // to figure-out what the max. height should be given the aspect ratio.
+                    adjustWidth = false;
+                    break;
+                case ORIENTATION_PORTRAIT:
+                    // Height should be the longer side for this portrait app, so we use the height
+                    // to figure-out what the max. width should be given the aspect ratio.
+                    adjustWidth = true;
+                    break;
+                default:
+                    // This app doesn't have a preferred orientation, so we keep the length of the
+                    // longer side, and use it to figure-out the length of the shorter side.
+                    if (containingAppWidth < containingAppHeight) {
+                        // Width is the shorter side, so we use the height to figure-out what the
+                        // max. width should be given the aspect ratio.
+                        adjustWidth = true;
+                    } else {
+                        // Height is the shorter side, so we use the width to figure-out what the
+                        // max. height should be given the aspect ratio.
+                        adjustWidth = false;
+                    }
+                    break;
+            }
+            if (adjustWidth) {
                 activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
             } else {
-                // Height is the shorter side, so we use the width to figure-out what the max.
-                // height should be given the aspect ratio.
                 activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0a3c2fb..6ffd554 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -978,12 +978,20 @@
             r.notifyUnknownVisibilityLaunched();
         }
 
-        // Post message to start process to avoid possible deadlock of calling into AMS with the
-        // ATMS lock held.
-        final Message msg = PooledLambda.obtainMessage(
-                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
-        mService.mH.sendMessage(msg);
+        try {
+            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
+                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+                        + r.processName);
+            }
+            // Post message to start process to avoid possible deadlock of calling into AMS with the
+            // ATMS lock held.
+            final Message msg = PooledLambda.obtainMessage(
+                    ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+                    r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
+            mService.mH.sendMessage(msg);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
@@ -2332,6 +2340,20 @@
             if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
                 throw new IllegalStateException("Task resolved to incompatible display");
             }
+
+            final ActivityDisplay preferredDisplay =
+                    mRootActivityContainer.getActivityDisplay(preferredDisplayId);
+
+            final boolean singleTaskInstance = preferredDisplay != null
+                    && preferredDisplay.isSingleTaskInstance();
+
+            if (singleTaskInstance) {
+                // Suppress the warning toast if the preferredDisplay was set to singleTask.
+                // The singleTaskInstance displays will only contain one task and any attempt to
+                // launch new task will re-route to the default display.
+                return;
+            }
+
             if (preferredDisplayId != actualDisplayId) {
                 Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
                 // Display a warning toast that we failed to put a task on a secondary display.
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 43c1206..b287a0b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -46,6 +46,7 @@
 import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
@@ -368,67 +369,70 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
+            intents = ArrayUtils.filterNotNull(intents, Intent[]::new);
+            final ActivityStarter[] starters = new ActivityStarter[intents.length];
+            // Do not hold WM global lock on this loop because when resolving intent, it may
+            // potentially acquire activity manager lock that leads to deadlock.
+            for (int i = 0; i < intents.length; i++) {
+                Intent intent = intents[i];
+
+                // Refuse possible leaked file descriptors.
+                if (intent.hasFileDescriptors()) {
+                    throw new IllegalArgumentException("File descriptors passed in Intent");
+                }
+
+                // Don't modify the client's object!
+                intent = new Intent(intent);
+
+                // Collect information about the target of the Intent.
+                ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
+                        0 /* startFlags */, null /* profilerInfo */, userId,
+                        ActivityStarter.computeResolveFilterUid(
+                                callingUid, realCallingUid, UserHandle.USER_NULL));
+                aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+
+                if (aInfo != null && (aInfo.applicationInfo.privateFlags
+                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
+                    throw new IllegalArgumentException("FLAG_CANT_SAVE_STATE not supported here");
+                }
+
+                final boolean top = i == intents.length - 1;
+                final SafeActivityOptions checkedOptions = top
+                        ? options
+                        : null;
+                starters[i] = obtainStarter(intent, reason)
+                        .setCaller(caller)
+                        .setResolvedType(resolvedTypes[i])
+                        .setActivityInfo(aInfo)
+                        .setResultTo(resultTo)
+                        .setRequestCode(-1)
+                        .setCallingPid(callingPid)
+                        .setCallingUid(callingUid)
+                        .setCallingPackage(callingPackage)
+                        .setRealCallingPid(realCallingPid)
+                        .setRealCallingUid(realCallingUid)
+                        .setActivityOptions(checkedOptions)
+                        .setComponentSpecified(intent.getComponent() != null)
+
+                        // Top activity decides on animation being run, so we allow only for the
+                        // top one as otherwise an activity below might consume it.
+                        .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
+                        .setOriginatingPendingIntent(originatingPendingIntent)
+                        .setAllowBackgroundActivityStart(allowBackgroundActivityStart);
+            }
+
+            final ActivityRecord[] outActivity = new ActivityRecord[1];
+            // Lock the loop to ensure the activities launched in a sequence.
             synchronized (mService.mGlobalLock) {
-                ActivityRecord[] outActivity = new ActivityRecord[1];
-                for (int i=0; i < intents.length; i++) {
-                    Intent intent = intents[i];
-                    if (intent == null) {
-                        continue;
+                for (int i = 0; i < starters.length; i++) {
+                    final int startResult = starters[i].setOutActivity(outActivity).execute();
+                    if (startResult < START_SUCCESS) {
+                        // Abort by error result and recycle unused starters.
+                        for (int j = i + 1; j < starters.length; j++) {
+                            mFactory.recycle(starters[j]);
+                        }
+                        return startResult;
                     }
-
-                    // Refuse possible leaked file descriptors
-                    if (intent != null && intent.hasFileDescriptors()) {
-                        throw new IllegalArgumentException("File descriptors passed in Intent");
-                    }
-
-                    boolean componentSpecified = intent.getComponent() != null;
-
-                    // Don't modify the client's object!
-                    intent = new Intent(intent);
-
-                    // Collect information about the target of the Intent.
-                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
-                            null, userId, ActivityStarter.computeResolveFilterUid(
-                                    callingUid, realCallingUid, UserHandle.USER_NULL));
-                    aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
-
-                    if (aInfo != null &&
-                            (aInfo.applicationInfo.privateFlags
-                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
-                        throw new IllegalArgumentException(
-                                "FLAG_CANT_SAVE_STATE not supported here");
-                    }
-
-                    final boolean top = i == intents.length - 1;
-                    final SafeActivityOptions checkedOptions = top
-                            ? options
-                            : null;
-                    final int res = obtainStarter(intent, reason)
-                            .setCaller(caller)
-                            .setResolvedType(resolvedTypes[i])
-                            .setActivityInfo(aInfo)
-                            .setResultTo(resultTo)
-                            .setRequestCode(-1)
-                            .setCallingPid(callingPid)
-                            .setCallingUid(callingUid)
-                            .setCallingPackage(callingPackage)
-                            .setRealCallingPid(realCallingPid)
-                            .setRealCallingUid(realCallingUid)
-                            .setActivityOptions(checkedOptions)
-                            .setComponentSpecified(componentSpecified)
-                            .setOutActivity(outActivity)
-
-                            // Top activity decides on animation being run, so we allow only for the
-                            // top one as otherwise an activity below might consume it.
-                            .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
-                            .setOriginatingPendingIntent(originatingPendingIntent)
-                            .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
-                            .execute();
-
-                    if (res < 0) {
-                        return res;
-                    }
-
                     resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c33a2c1..538813e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
@@ -115,6 +116,7 @@
 import android.util.Slog;
 import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
@@ -761,11 +763,11 @@
             abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
             // TODO: remove this toast after feature development is done
             if (abortBackgroundStart) {
-                final String toastMsg = abort
-                        ? "Background activity start from " + callingPackage
-                                + " blocked. See go/q-bg-block."
-                        : "This background activity start from " + callingPackage
-                                + " will be blocked in future Q builds. See go/q-bg-block.";
+                final Resources res = mService.mContext.getResources();
+                final String toastMsg = res.getString(abort
+                            ? R.string.activity_starter_block_bg_activity_starts_enforcing
+                            : R.string.activity_starter_block_bg_activity_starts_permissive,
+                        callingPackage);
                 mService.mUiHandler.post(() -> {
                     Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show();
                 });
@@ -830,12 +832,25 @@
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
 
-                final int flags = intent.getFlags();
                 Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.setFlags(flags
-                        | FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+                int flags = intent.getFlags();
+                flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
+                /*
+                 * Prevent reuse of review activity: Each app needs their own review activity. By
+                 * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities
+                 * with the same launch parameters (extras are ignored). Hence to avoid possible
+                 * reuse force a new activity via the MULTIPLE_TASK flag.
+                 *
+                 * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used,
+                 * hence no need to add the flag in this case.
+                 */
+                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) {
+                    flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+                }
+                newIntent.setFlags(flags);
+
                 newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                 newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                 if (resultRecord != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 258819f..9b35e85 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2467,7 +2467,7 @@
                 }
                 final Rect destBounds = new Rect();
                 stack.getAnimationOrCurrentBounds(destBounds);
-                if (!destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
+                if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
                     Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete.");
                     return;
                 }
@@ -3202,23 +3202,34 @@
     }
 
     @Override
-    public void exitFreeformMode(IBinder token) {
+    public void toggleFreeformWindowingMode(IBinder token) {
         synchronized (mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
                 final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                 if (r == null) {
                     throw new IllegalArgumentException(
-                            "exitFreeformMode: No activity record matching token=" + token);
+                            "toggleFreeformWindowingMode: No activity record matching token="
+                                    + token);
                 }
 
                 final ActivityStack stack = r.getActivityStack();
-                if (stack == null || !stack.inFreeformWindowingMode()) {
-                    throw new IllegalStateException(
-                            "exitFreeformMode: You can only go fullscreen from freeform.");
+                if (stack == null) {
+                    throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
+                            + "doesn't have a stack");
                 }
 
-                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                if (!stack.inFreeformWindowingMode()
+                        && stack.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+                    throw new IllegalStateException("toggleFreeformWindowingMode: You can only "
+                            + "toggle between fullscreen and freeform.");
+                }
+
+                if (stack.inFreeformWindowingMode()) {
+                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                } else {
+                    stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+                }
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5923,14 +5934,12 @@
                 Binder.restoreCallingIdentity(ident);
             }
 
-            synchronized (mGlobalLock) {
-                return getActivityStartController().startActivitiesInPackage(
-                        packageUid, packageName,
-                        intents, resolvedTypes, null /* resultTo */,
-                        SafeActivityOptions.fromBundle(bOptions), userId,
-                        false /* validateIncomingUser */, null /* originatingPendingIntent */,
-                        false /* allowBackgroundActivityStart */);
-            }
+            return getActivityStartController().startActivitiesInPackage(
+                    packageUid, packageName,
+                    intents, resolvedTypes, null /* resultTo */,
+                    SafeActivityOptions.fromBundle(bOptions), userId,
+                    false /* validateIncomingUser */, null /* originatingPendingIntent */,
+                    false /* allowBackgroundActivityStart */);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 6dc73bb..19ff438 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1879,6 +1879,9 @@
             mNextAppTransitionAnimationsSpecsFuture = specsFuture;
             mNextAppTransitionScaleUp = scaleUp;
             mNextAppTransitionFutureCallback = callback;
+            if (isReady()) {
+                fetchAppTransitionSpecsFromFuture();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 97062a6..5c528c7 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -568,7 +568,7 @@
 
         final long token = proto.start(fieldId);
         mRequestedOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION,
-                logLevel != WindowTraceLogLevel.CRITICAL);
+                logLevel == WindowTraceLogLevel.CRITICAL);
         if (logLevel == WindowTraceLogLevel.ALL) {
             mFullConfiguration.writeToProto(proto, FULL_CONFIGURATION, false /* critical */);
             mMergedOverrideConfiguration.writeToProto(proto, MERGED_OVERRIDE_CONFIGURATION,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5cfc20b..4795555 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1500,8 +1500,8 @@
         final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
         final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
 
-        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
         mDisplayPolicy.configure(width, height, shortSizeDp);
+        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2ee30ac..91d573d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2617,9 +2617,8 @@
             DisplayCutout displayCutout) {
         int width = fullWidth;
         if (hasNavigationBar()) {
-            // For a basic navigation bar, when we are in landscape mode we place
-            // the navigation bar to the side.
-            if (navigationBarCanMove() && fullWidth > fullHeight) {
+            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+            if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
                 width -= getNavigationBarWidth(rotation, uiMode);
             }
         }
@@ -2646,9 +2645,8 @@
             DisplayCutout displayCutout) {
         int height = fullHeight;
         if (hasNavigationBar()) {
-            // For a basic navigation bar, when we are in portrait mode we place
-            // the navigation bar to the bottom.
-            if (!navigationBarCanMove() || fullWidth < fullHeight) {
+            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+            if (navBarPosition == NAV_BAR_BOTTOM) {
                 height -= getNavigationBarHeight(rotation, uiMode);
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 5f341ee..543f196 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -38,6 +38,7 @@
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.DisplayCutout;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,8 @@
     private final int mDeskDockRotation;
     private final int mUndockedHdmiRotation;
 
+    private final float mCloseToSquareMaxAspectRatio;
+
     private OrientationListener mOrientationListener;
     private StatusBarManagerInternal mStatusBarManagerInternal;
     private SettingsObserver mSettingsObserver;
@@ -132,6 +135,9 @@
         mUndockedHdmiRotation = readRotation(
                 com.android.internal.R.integer.config_undockedHdmiRotation);
 
+        mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio);
+
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
             mOrientationListener = new OrientationListener(mContext, uiHandler);
@@ -212,10 +218,12 @@
         // so if the orientation is forced, we need to respect that no matter what.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
+        final boolean isCloseToSquare =
+                isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height);
         final boolean forceDefaultOrientationInRes =
                 res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
         final boolean forceDefaultOrienation =
-                ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+                ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare)
                         && forceDefaultOrientationInRes
                         // For debug purposes the next line turns this feature off with:
                         // $ adb shell setprop config.override_forced_orient true
@@ -227,6 +235,18 @@
         setFixedToUserRotation(forceDefaultOrienation);
     }
 
+    private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) {
+        final DisplayCutout displayCutout =
+                mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
+        final int uiMode = mService.mPolicy.getUiMode();
+        final int w = mDisplayPolicy.getNonDecorDisplayWidth(
+                width, height, rotation, uiMode, displayCutout);
+        final int h = mDisplayPolicy.getNonDecorDisplayHeight(
+                width, height, rotation, uiMode, displayCutout);
+        final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
+        return aspectRatio <= mCloseToSquareMaxAspectRatio;
+    }
+
     void setRotation(int rotation) {
         if (mOrientationListener != null) {
             mOrientationListener.setCurrentRotation(rotation);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ea65dd9..1d76a71 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -396,6 +396,10 @@
         if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
                 || mAdjustedForDivider != adjustedForDivider) {
             if (animate && !mAnimatingForMinimizedDockedStack) {
+                // Notify SystemUI to set the target docked stack size according current docked
+                // state without animation when calling startImeAdjustAnimation.
+                notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
+                        isHomeStackResizable());
                 startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
             } else {
                 // Animation might be delayed, so only notify if we don't run an animation.
@@ -889,7 +893,10 @@
         }
         if (mAnimatingForMinimizedDockedStack) {
             return animateForMinimizedDockedStack(now);
-        } else if (mAnimatingForIme) {
+        } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) {
+            // To prevent task stack resize animation may flicking when playing app transition
+            // animation & IME window enter animation in parallel, make sure app transition is done
+            // and then start to animate for IME.
             return animateForIme(now);
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f9c9d33..f46835e 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -163,15 +163,12 @@
         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
     }
 
-    /**
-     * Provides an opportunity for the window manager policy to intercept early motion event
-     * processing when the device is in a non-interactive state since these events are normally
-     * dropped.
-     */
+    /** {@inheritDoc} */
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
+            int policyFlags) {
         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
-                whenNanos, policyFlags);
+                displayId, whenNanos, policyFlags);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index fb5b1d8..6988357 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -300,7 +300,7 @@
      * corresponding record in display manager.
      */
     // TODO: Look into consolidating with getActivityDisplay()
-    ActivityDisplay getActivityDisplayOrCreate(int displayId) {
+    @Nullable ActivityDisplay getActivityDisplayOrCreate(int displayId) {
         ActivityDisplay activityDisplay = getActivityDisplay(displayId);
         if (activityDisplay != null) {
             return activityDisplay;
@@ -1126,6 +1126,13 @@
                 if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
                     continue;
                 }
+                if (stack == targetStack) {
+                    // Simply update the result for targetStack because the targetStack had
+                    // already resumed in above. We don't want to resume it again, especially in
+                    // some cases, it would cause a second launch failure if app process was dead.
+                    resumedOnDisplay |= result;
+                    continue;
+                }
                 if (topRunningActivity.isState(RESUMED)) {
                     // Kick off any lingering app transitions form the MoveTaskToFront operation.
                     stack.executeAppTransition(targetOptions);
@@ -1317,6 +1324,9 @@
         if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
         synchronized (mService.mGlobalLock) {
             final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
+            if (display == null) {
+                return;
+            }
             // Do not start home before booting, or it may accidentally finish booting before it
             // starts. Instead, we expect home activities to be launched when the system is ready
             // (ActivityManagerService#systemReady).
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 59d7560..9d3112f 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -2134,7 +2134,7 @@
             if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
                 final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
                 inOutConfig.screenHeightDp = insideParentBounds
-                        ? Math.min(overrideScreenHeightDp, parentConfig.screenWidthDp)
+                        ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
                         : overrideScreenHeightDp;
             }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7b742fd..b915199 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1163,6 +1163,14 @@
     }
 
     private boolean adjustForIME(final WindowState imeWin) {
+        // To prevent task stack resize animation may flicking when playing app transition
+        // animation & IME window enter animation in parallel, we need to make sure app
+        // transition is done and then adjust task size for IME, skip the new adjusted frame when
+        // app transition is still running.
+        if (getDisplayContent().mAppTransition.isRunning()) {
+            return false;
+        }
+
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
         if (imeWin == null || !dockedTopOrBottom) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c747c6d..7beee0e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
 import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
@@ -446,7 +447,8 @@
     final boolean mLimitedAlphaCompositing;
     final int mMaxUiWidth;
 
-    final WindowManagerPolicy mPolicy;
+    @VisibleForTesting
+    WindowManagerPolicy mPolicy;
 
     final IActivityManager mActivityManager;
     // TODO: Probably not needed once activities are fully in WM.
@@ -3758,6 +3760,41 @@
         mPolicy.unregisterDisplayFoldListener(listener);
     }
 
+    /**
+     * Overrides the folded area.
+     *
+     * @param area the overriding folded area or an empty {@code Rect} to clear the override.
+     */
+    void setOverrideFoldedArea(@NonNull Rect area) {
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
+        }
+
+        long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mPolicy.setOverrideFoldedArea(area);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
+     * Get the display folded area.
+     */
+    @NonNull Rect getFoldedArea() {
+        long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mPolicy.getFoldedArea();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     @Override
     public int getPreferredOptionsPanelGravity(int displayId) {
         synchronized (mGlobalLock) {
@@ -4263,9 +4300,12 @@
             if (mMaxUiWidth > 0) {
                 mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
             }
-            applyForcedPropertiesForDefaultDisplay();
+            final boolean changed = applyForcedPropertiesForDefaultDisplay();
             mAnimator.ready();
             mDisplayReady = true;
+            if (changed) {
+                reconfigureDisplayLocked(getDefaultDisplayContentLocked());
+            }
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
         }
@@ -4822,11 +4862,9 @@
 
     @Override
     public void setForcedDisplaySize(int displayId, int width, int height) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
         final long ident = Binder.clearCallingIdentity();
@@ -4844,11 +4882,9 @@
 
     @Override
     public void setForcedDisplayScalingMode(int displayId, int mode) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
         final long ident = Binder.clearCallingIdentity();
@@ -4865,7 +4901,8 @@
     }
 
     /** The global settings only apply to default display. */
-    private void applyForcedPropertiesForDefaultDisplay() {
+    private boolean applyForcedPropertiesForDefaultDisplay() {
+        boolean changed = false;
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
         // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
@@ -4885,6 +4922,7 @@
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
                         displayContent.updateBaseDisplayMetrics(width, height,
                                 displayContent.mBaseDisplayDensity);
+                        changed = true;
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -4893,26 +4931,27 @@
 
         // Display density.
         final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
-        if (density != 0) {
+        if (density != 0 && density != displayContent.mBaseDisplayDensity) {
             displayContent.mBaseDisplayDensity = density;
+            changed = true;
         }
 
         // Display scaling mode.
         int mode = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SCALING_FORCE, 0);
-        if (mode != 0) {
+        if (displayContent.mDisplayScalingDisabled != (mode != 0)) {
             Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
             displayContent.mDisplayScalingDisabled = true;
+            changed = true;
         }
+        return changed;
     }
 
     @Override
     public void clearForcedDisplaySize(int displayId) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
         final long ident = Binder.clearCallingIdentity();
@@ -4953,11 +4992,9 @@
 
     @Override
     public void setForcedDisplayDensityForUser(int displayId, int density, int userId) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
         final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -4978,11 +5015,9 @@
 
     @Override
     public void clearForcedDisplayDensityForUser(int displayId, int userId) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
         final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -5047,11 +5082,9 @@
 
     @Override
     public void setOverscan(int displayId, int left, int top, int right, int bottom) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " +
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -5081,11 +5114,7 @@
 
     @Override
     public void startWindowTrace(){
-        try {
-            mWindowTracing.startTrace(null /* printwriter */);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        mWindowTracing.startTrace(null /* printwriter */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 83e3c71..d13ee45 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -62,6 +62,8 @@
                     return runDisplaySize(pw);
                 case "density":
                     return runDisplayDensity(pw);
+                case "folded-area":
+                    return runDisplayFoldedArea(pw);
                 case "overscan":
                     return runDisplayOverscan(pw);
                 case "scaling":
@@ -207,6 +209,40 @@
         return 0;
     }
 
+    private void printFoldedArea(PrintWriter pw) {
+        final Rect foldedArea = mInternal.getFoldedArea();
+        if (foldedArea.isEmpty()) {
+            pw.println("Folded area: none");
+        } else {
+            pw.println("Folded area: " + foldedArea.left + "," + foldedArea.top + ","
+                    + foldedArea.right + "," + foldedArea.bottom);
+        }
+    }
+
+    private int runDisplayFoldedArea(PrintWriter pw) {
+        final String areaStr = getNextArg();
+        final Rect rect = new Rect();
+        if (areaStr == null) {
+            printFoldedArea(pw);
+            return 0;
+        } else if ("reset".equals(areaStr)) {
+            rect.setEmpty();
+        } else {
+            final Pattern flattenedPattern = Pattern.compile(
+                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
+            final Matcher matcher = flattenedPattern.matcher(areaStr);
+            if (!matcher.matches()) {
+                getErrPrintWriter().println("Error: area should be LEFT,TOP,RIGHT,BOTTOM");
+                return -1;
+            }
+            rect.set(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)),
+                    Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4)));
+        }
+
+        mInternal.setOverrideFoldedArea(rect);
+        return 0;
+    }
+
     private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
         String overscanStr = getNextArgRequired();
         Rect rect = new Rect();
@@ -335,6 +371,8 @@
         pw.println("    width and height in pixels unless suffixed with 'dp'.");
         pw.println("  density [reset|DENSITY] [-d DISPLAY_ID]");
         pw.println("    Return or override display density.");
+        pw.println("  folded-area [reset|LEFT,TOP,RIGHT,BOTTOM]");
+        pw.println("    Return or override folded area.");
         pw.println("  overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]");
         pw.println("    Set overscan area for display.");
         pw.println("  scaling [off|auto] [-d DISPLAY_ID]");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48cd6b6..21a557e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2164,9 +2164,6 @@
                 // A modal window uses the whole compatibility bounds.
                 flags |= FLAG_NOT_TOUCH_MODAL;
                 mTmpRect.set(mAppToken.getResolvedOverrideBounds());
-                // TODO(b/112288258): Remove the forced offset when the override bounds always
-                // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}).
-                mTmpRect.offsetTo(0, 0);
             } else {
                 // Non-modal uses the application based frame.
                 mTmpRect.set(mWindowFrames.mCompatFrame);
@@ -2466,7 +2463,7 @@
     /** @return false if this window desires touch events. */
     boolean cantReceiveTouchInput() {
         return mAppToken != null && mAppToken.getTask() != null
-                && mAppToken.getTask().mStack.shouldIgnoreInput();
+                && (mAppToken.getTask().mStack.shouldIgnoreInput() || mAppToken.hiddenRequested);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index e4461ea..2ce6e6c 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
 
-import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,24 +35,30 @@
 /**
  * Buffer used for window tracing.
  */
-abstract class WindowTraceBuffer {
+class WindowTraceBuffer {
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
 
-    final Object mBufferLock = new Object();
-    final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
-    final File mTraceFile;
-    int mBufferSize;
-    private final int mBufferCapacity;
+    private final Object mBufferLock = new Object();
 
-    WindowTraceBuffer(int size, File traceFile) throws IOException {
-        mBufferCapacity = size;
-        mTraceFile = traceFile;
+    private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
+    private int mBufferUsedSize;
+    private int mBufferCapacity;
 
-        initTraceFile();
+    WindowTraceBuffer(int bufferCapacity) {
+        mBufferCapacity = bufferCapacity;
+        resetBuffer();
     }
 
     int getAvailableSpace() {
-        return mBufferCapacity - mBufferSize;
+        return mBufferCapacity - mBufferUsedSize;
+    }
+
+    int size() {
+        return mBuffer.size();
+    }
+
+    void setCapacity(int capacity) {
+        mBufferCapacity = capacity;
     }
 
     /**
@@ -70,42 +75,37 @@
                     + mBufferCapacity + " Object size: " + protoLength);
         }
         synchronized (mBufferLock) {
-            boolean canAdd = canAdd(protoLength);
-            if (canAdd) {
-                mBuffer.add(proto);
-                mBufferSize += protoLength;
-            }
+            discardOldest(protoLength);
+            mBuffer.add(proto);
+            mBufferUsedSize += protoLength;
             mBufferLock.notify();
         }
     }
 
-    /**
-     * Stops the buffer execution and flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
-     */
-    void dump() throws IOException, InterruptedException {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile");
-            writeTraceToFile();
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    @VisibleForTesting
     boolean contains(byte[] other) {
         return mBuffer.stream()
                 .anyMatch(p -> Arrays.equals(p.getBytes(), other));
     }
 
-    private void initTraceFile() throws IOException {
-        mTraceFile.delete();
-        try (OutputStream os = new FileOutputStream(mTraceFile)) {
-            mTraceFile.setReadable(true, false);
-            ProtoOutputStream proto = new ProtoOutputStream(os);
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.flush();
+    /**
+     * Writes the trace buffer to disk.
+     */
+    void writeTraceToFile(File traceFile) throws IOException {
+        synchronized (mBufferLock) {
+            traceFile.delete();
+            traceFile.setReadable(true, false);
+            try (OutputStream os = new FileOutputStream(traceFile)) {
+                ProtoOutputStream proto = new ProtoOutputStream();
+                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+                os.write(proto.getBytes());
+                while (!mBuffer.isEmpty()) {
+                    proto = mBuffer.poll();
+                    mBufferUsedSize -= proto.getRawSize();
+                    byte[] protoBytes = proto.getBytes();
+                    os.write(protoBytes);
+                }
+                os.flush();
+            }
         }
     }
 
@@ -114,59 +114,48 @@
      * smaller than the overall buffer size.
      *
      * @param protoLength byte array representation of the Proto object to add
-     * @return {@code true} if the element can be added to the buffer or not
      */
-    abstract boolean canAdd(int protoLength);
+    private void discardOldest(int protoLength) {
+        long availableSpace = getAvailableSpace();
+
+        while (availableSpace < protoLength) {
+
+            ProtoOutputStream item = mBuffer.poll();
+            if (item == null) {
+                throw new IllegalStateException("No element to discard from buffer");
+            }
+            mBufferUsedSize -= item.getRawSize();
+            availableSpace = getAvailableSpace();
+        }
+    }
 
     /**
-     * Flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+     * Removes all elements form the buffer
      */
-    abstract void writeTraceToFile() throws IOException, InterruptedException;
-
-    /**
-     * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for
-     * continuous mode or a {@link WindowTraceQueueBuffer} otherwise
-     */
-    static class Builder {
-        private boolean mContinuous;
-        private File mTraceFile;
-        private int mBufferCapacity;
-
-        Builder setContinuousMode(boolean continuous) {
-            mContinuous = continuous;
-            return this;
+    void resetBuffer() {
+        synchronized (mBufferLock) {
+            mBuffer.clear();
+            mBufferUsedSize = 0;
         }
+    }
 
-        Builder setTraceFile(File traceFile) {
-            mTraceFile = traceFile;
-            return this;
-        }
+    @VisibleForTesting
+    int getBufferSize() {
+        return mBufferUsedSize;
+    }
 
-        Builder setBufferCapacity(int size) {
-            mBufferCapacity = size;
-            return this;
-        }
-
-        File getFile() {
-            return mTraceFile;
-        }
-
-        WindowTraceBuffer build() throws IOException {
-            if (mBufferCapacity <= 0) {
-                throw new IllegalStateException("Buffer capacity must be greater than 0.");
-            }
-
-            if (mTraceFile == null) {
-                throw new IllegalArgumentException("A valid trace file must be specified.");
-            }
-
-            if (mContinuous) {
-                return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile);
-            } else {
-                return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
-            }
+    String getStatus() {
+        synchronized (mBufferLock) {
+            return "Buffer size: "
+                    + mBufferCapacity
+                    + " bytes"
+                    + "\n"
+                    + "Buffer usage: "
+                    + mBufferUsedSize
+                    + " bytes"
+                    + "\n"
+                    + "Elements in the buffer: "
+                    + mBuffer.size();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
deleted file mode 100644
index 5888b7a..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 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.os.Build.IS_USER;
-
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
- * {@code #size size} bytes of window trace elements.
- * Once the buffer is full it will no longer accepts new elements.
- */
-class WindowTraceQueueBuffer extends WindowTraceBuffer {
-    private static final String TAG = "WindowTracing";
-
-    private Thread mConsumerThread;
-    private boolean mCancel;
-
-    @VisibleForTesting
-    WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread)
-            throws IOException {
-        super(size, traceFile);
-        if (startConsumerThread) {
-            initializeConsumerThread();
-        }
-    }
-
-    WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
-        this(size, traceFile, !IS_USER);
-    }
-
-    private void initializeConsumerThread() {
-        mCancel = false;
-        mConsumerThread = new Thread(() -> {
-            try {
-                loop();
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Interrupting trace consumer thread");
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to execute trace consumer thread", e);
-            }
-        }, "window_tracing");
-        mConsumerThread.start();
-    }
-
-    private void loop() throws IOException, InterruptedException {
-        while (!mCancel) {
-            ProtoOutputStream proto;
-            synchronized (mBufferLock) {
-                mBufferLock.wait();
-                proto = mBuffer.poll();
-                if (proto != null) {
-                    mBufferSize -= proto.getRawSize();
-                }
-            }
-            if (proto != null) {
-                try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-        return availableSpace >= protoLength;
-    }
-
-    @Override
-    void writeTraceToFile() throws InterruptedException {
-        synchronized (mBufferLock) {
-            mCancel = true;
-            mBufferLock.notify();
-        }
-        if (mConsumerThread != null) {
-            mConsumerThread.join();
-            mConsumerThread = null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
deleted file mode 100644
index 77d30be..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 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 android.util.proto.ProtoOutputStream;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A ring buffer to store the {@code #size size} bytes of window trace data.
- * The buffer operates on a trace entry level, that is, if the new trace data is larger than the
- * available buffer space, the buffer will discard as many full trace entries as necessary to fit
- * the new trace.
- */
-class WindowTraceRingBuffer extends WindowTraceBuffer {
-    WindowTraceRingBuffer(int size, File traceFile) throws IOException {
-        super(size, traceFile);
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-
-        while (availableSpace < protoLength) {
-            discardOldest();
-            availableSpace = getAvailableSpace();
-        }
-
-        return true;
-    }
-
-    @Override
-    void writeTraceToFile() throws IOException {
-        synchronized (mBufferLock) {
-            try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                while (!mBuffer.isEmpty()) {
-                    ProtoOutputStream proto = mBuffer.poll();
-                    mBufferSize -= proto.getRawSize();
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    private void discardOldest() {
-        ProtoOutputStream item = mBuffer.poll();
-        if (item == null) {
-            throw new IllegalStateException("No element to discard from buffer");
-        }
-        mBufferSize -= item.getRawSize();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index abc474d..0ce215c 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -31,8 +31,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -47,139 +45,191 @@
      * Maximum buffer size, currently defined as 512 KB
      * Size was experimentally defined to fit between 100 to 150 elements.
      */
-    private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
+    private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
+    private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
+    private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
+    private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
     private static final String TAG = "WindowTracing";
 
     private final WindowManagerService mService;
     private final Choreographer mChoreographer;
     private final WindowManagerGlobalLock mGlobalLock;
 
-    private final Object mLock = new Object();
-    private final WindowTraceBuffer.Builder mBufferBuilder;
+    private final Object mEnabledLock = new Object();
+    private final File mTraceFile;
+    private final WindowTraceBuffer mBuffer;
+    private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+            log("onFrame" /* where */);
 
-    private WindowTraceBuffer mTraceBuffer;
-
-    private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM;
-    private boolean mContinuousMode;
+    private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+    private boolean mLogOnFrame = false;
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
     private boolean mScheduled;
-    private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
-            log("onFrame" /* where */);
 
-    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
-        this(file, service, choreographer, service.mGlobalLock);
+    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+            Choreographer choreographer) {
+        File file = new File(TRACE_FILENAME);
+        return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM);
     }
 
-    @VisibleForTesting
-    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
-            WindowManagerGlobalLock globalLock) {
-        mBufferBuilder = new WindowTraceBuffer.Builder()
-                .setTraceFile(file)
-                .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            int bufferCapacity) {
+        this(file, service, choreographer, service.mGlobalLock, bufferCapacity);
+    }
 
+    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock, int bufferCapacity) {
         mChoreographer = choreographer;
         mService = service;
         mGlobalLock = globalLock;
+        mTraceFile = file;
+        mBuffer = new WindowTraceBuffer(bufferCapacity);
+        setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
     }
 
-    void startTrace(@Nullable PrintWriter pw) throws IOException {
+    void startTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
-            if (mTraceBuffer != null) {
-                writeTraceToFileLocked();
-            }
-            mTraceBuffer = mBufferBuilder
-                    .setContinuousMode(mContinuousMode)
-                    .build();
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+            mBuffer.resetBuffer();
             mEnabled = mEnabledLockFree = true;
         }
     }
 
-    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Log.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     void stopTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
-                    + ". Waiting for traces to flush.");
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
             mEnabled = mEnabledLockFree = false;
 
-            synchronized (mLock) {
-                if (mEnabled) {
-                    logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
-                    throw new IllegalStateException("tracing enabled while waiting for flush.");
-                }
-                writeTraceToFileLocked();
-                mTraceBuffer = null;
+            if (mEnabled) {
+                logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+                throw new IllegalStateException("tracing enabled while waiting for flush.");
             }
-            logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
         }
     }
 
-    @VisibleForTesting
-    void setContinuousMode(boolean continuous, PrintWriter pw) {
-        logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
+    private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
+        mLogLevel = logLevel;
 
-        if (mEnabled) {
-            logAndPrintln(pw, "Trace is currently active, change will take effect once the "
-                    + "trace is restarted.");
+        switch (logLevel) {
+            case WindowTraceLogLevel.ALL: {
+                setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
+                break;
+            }
+            case WindowTraceLogLevel.TRIM: {
+                setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
+                break;
+            }
+            case WindowTraceLogLevel.CRITICAL: {
+                setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
+                break;
+            }
         }
-        mContinuousMode = continuous;
-        mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL :
-                WindowTraceLogLevel.TRIM;
+    }
+
+    private void setLogFrequency(boolean onFrame, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log frequency to "
+                + ((onFrame) ? "frame" : "transaction"));
+        mLogOnFrame = onFrame;
+    }
+
+    private void setBufferCapacity(int capacity, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes");
+        mBuffer.setCapacity(capacity);
     }
 
     boolean isEnabled() {
         return mEnabledLockFree;
     }
 
-    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
-            Choreographer choreographer) {
-        File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        return new WindowTracing(file, service, choreographer);
-    }
-
     int onShellCommand(ShellCommand shell) {
         PrintWriter pw = shell.getOutPrintWriter();
-        try {
-            String cmd = shell.getNextArgRequired();
-            switch (cmd) {
-                case "start":
-                    startTrace(pw);
-                    return 0;
-                case "stop":
-                    stopTrace(pw);
-                    return 0;
-                case "continuous":
-                    setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw);
-                    return 0;
-                default:
-                    pw.println("Unknown command: " + cmd);
-                    return -1;
-            }
-        } catch (IOException e) {
-            logAndPrintln(pw, e.toString());
-            throw new RuntimeException(e);
+        String cmd = shell.getNextArgRequired();
+        switch (cmd) {
+            case "start":
+                startTrace(pw);
+                return 0;
+            case "stop":
+                stopTrace(pw);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "frame":
+                setLogFrequency(true /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "transaction":
+                setLogFrequency(false /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "level":
+                String logLevelStr = shell.getNextArgRequired().toLowerCase();
+                switch (logLevelStr) {
+                    case "all": {
+                        setLogLevel(WindowTraceLogLevel.ALL, pw);
+                        break;
+                    }
+                    case "trim": {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                    case "critical": {
+                        setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+                        break;
+                    }
+                    default: {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                }
+                mBuffer.resetBuffer();
+                return 0;
+            case "size":
+                setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Window manager trace options:");
+                pw.println("  start: Start logging");
+                pw.println("  stop: Stop logging");
+                pw.println("  frame: Log trace once per frame");
+                pw.println("  transaction: Log each transaction");
+                pw.println("  size: Set the maximum log size (in KB)");
+                pw.println("  level [lvl]: Set the log level between");
+                pw.println("    lvl may be one of:");
+                pw.println("      critical: Only visible windows with reduced information");
+                pw.println("      trim: All windows with reduced");
+                pw.println("      all: All window and information");
+                return -1;
         }
     }
 
+    private String getStatus() {
+        return "Status: "
+                + ((isEnabled()) ? "Enabled" : "Disabled")
+                + "\n"
+                + "Log level: "
+                + mLogLevel
+                + "\n"
+                + mBuffer.getStatus();
+    }
+
     /**
      * If tracing is enabled, log the current state or schedule the next frame to be logged,
-     * according to {@link #mContinuousMode}.
+     * according to {@link #mLogOnFrame}.
      *
      * @param where Logging point descriptor
      */
@@ -188,7 +238,7 @@
             return;
         }
 
-        if (mContinuousMode) {
+        if (mLogOnFrame) {
             schedule();
         } else {
             log(where);
@@ -215,25 +265,24 @@
     private void log(String where) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
         try {
-            synchronized (mGlobalLock) {
-                ProtoOutputStream os = new ProtoOutputStream();
-                long tokenOuter = os.start(ENTRY);
-                os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-                os.write(WHERE, where);
+            ProtoOutputStream os = new ProtoOutputStream();
+            long tokenOuter = os.start(ENTRY);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+            os.write(WHERE, where);
 
+            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+            synchronized (mGlobalLock) {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
                 try {
-                    long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
-                    mService.writeToProtoLocked(os, mWindowTraceLogLevel);
-                    os.end(tokenInner);
+                    mService.writeToProtoLocked(os, mLogLevel);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 }
-                os.end(tokenOuter);
-                mTraceBuffer.add(os);
-
-                mScheduled = false;
             }
+            os.end(tokenInner);
+            os.end(tokenOuter);
+            mBuffer.add(os);
+            mScheduled = false;
         } catch (Exception e) {
             Log.wtf(TAG, "Exception while tracing state", e);
         } finally {
@@ -242,31 +291,37 @@
     }
 
     /**
-     * Writes the trace buffer to disk. This method has no internal synchronization and should be
-     * externally synchronized
-     */
-    private void writeTraceToFileLocked() {
-        if (mTraceBuffer == null) {
-            return;
-        }
-
-        try {
-            mTraceBuffer.dump();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write buffer to file", e);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
-        }
-    }
-
-    /**
-     * Writes the trace buffer to disk and clones it into a new file for the bugreport.
+     * Writes the trace buffer to new file for the bugreport.
+     *
      * This method is synchronized with {@code #startTrace(PrintWriter)} and
      * {@link #stopTrace(PrintWriter)}.
      */
     void writeTraceToFile() {
-        synchronized (mLock) {
+        synchronized (mEnabledLock) {
             writeTraceToFileLocked();
         }
     }
-}
+
+    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Log.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+
+    /**
+     * Writes the trace buffer to disk. This method has no internal synchronization and should be
+     * externally synchronized
+     */
+    private void writeTraceToFileLocked() {
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
+            mBuffer.writeTraceToFile(mTraceFile);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 5c19ad3..9cbb58d 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "BatteryStatsService"
 //#define LOG_NDEBUG 0
 
+#include <climits>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -28,6 +29,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <unordered_map>
+#include <utility>
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
@@ -87,6 +89,15 @@
 std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
 std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
 
+// Cellular/Wifi power monitor rail information
+static jmethodID jupdateRailData = NULL;
+static jmethodID jsetRailStatsAvailability = NULL;
+
+std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
+
+std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {};
+static bool power_monitor_available = false;
+
 // The caller must be holding gPowerHalMutex.
 static void deinitPowerStatsLocked() {
     gPowerStatsHalV1_0 = nullptr;
@@ -258,6 +269,7 @@
     gPowerStatsHalStateNames.clear();
     gPowerStatsHalPlatformIds.clear();
     gPowerStatsHalSubsystemIds.clear();
+    gPowerStatsHalRailNames.clear();
 
     Return<void> ret;
     ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
@@ -301,6 +313,27 @@
         return false;
     }
 
+    // Get Power monitor rails available
+    ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGW("Rail information is not available");
+            power_monitor_available = false;
+            return;
+        }
+
+        // Fill out rail names/subsystems into gPowerStatsHalRailNames
+        for (auto rail : rails) {
+            gPowerStatsHalRailNames.emplace(rail.index,
+                std::make_pair(rail.railName, rail.subsysName));
+        }
+        if (!gPowerStatsHalRailNames.empty()) {
+            power_monitor_available = true;
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
     return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
 }
 
@@ -517,6 +550,50 @@
     return total_added;
 }
 
+static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) {
+    using android::hardware::power::stats::V1_0::Status;
+    using android::hardware::power::stats::V1_0::EnergyData;
+
+    if (!getPowerStatsHalLocked()) {
+        ALOGE("failed to get power stats");
+        return;
+    }
+
+    if (!power_monitor_available) {
+        env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+        ALOGW("Rail energy data is not available");
+        return;
+    }
+
+    // Get power rail energySinceBoot data
+    Return<void> ret = gPowerStatsHalV1_0->getEnergyData({},
+        [&env, &jrailStats](auto energyData, auto status) {
+            if (status == Status::NOT_SUPPORTED) {
+                ALOGW("getEnergyData is not supported");
+                return;
+            }
+
+            for (auto data : energyData) {
+                if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) {
+                    env->CallVoidMethod(jrailStats,
+                        jupdateRailData,
+                        data.index,
+                        env->NewStringUTF(
+                            gPowerStatsHalRailNames.at(data.index).first.c_str()),
+                        env->NewStringUTF(
+                            gPowerStatsHalRailNames.at(data.index).second.c_str()),
+                        data.timestamp,
+                        data.energy);
+                } else {
+                    ALOGE("Java long overflow seen. Rail index %d not updated", data.index);
+                }
+            }
+        });
+    if (!checkResultLocked(ret, __func__)) {
+        ALOGE("getEnergyData failed");
+    }
+}
+
 // The caller must be holding powerHalMutex.
 static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
     sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
@@ -761,11 +838,13 @@
         gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData;
         gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData;
         gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData;
+        gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData;
     } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) {
         ALOGI("Using power HAL");
         gGetLowPowerStatsImpl = getPowerHalLowPowerData;
         gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
         gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+        gGetRailEnergyPowerStatsImpl = NULL;
     }
 }
 
@@ -835,11 +914,44 @@
     return -1;
 }
 
+static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
+    if (jrailStats == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "The railstats jni input jobject jrailStats is null.");
+        return;
+    }
+    if (jupdateRailData == NULL) {
+        ALOGE("A railstats jni jmethodID is null.");
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if (!gGetRailEnergyPowerStatsImpl) {
+        setUpPowerStatsLocked();
+    }
+
+    if (gGetRailEnergyPowerStatsImpl)  {
+        gGetRailEnergyPowerStatsImpl(env, jrailStats);
+        return;
+    }
+
+    if (jsetRailStatsAvailability == NULL) {
+        ALOGE("setRailStatsAvailability jni jmethodID is null.");
+        return;
+    }
+    env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+    ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false");
+    return;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
     { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
     { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
     { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
+    { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
+        (void*)getRailEnergyPowerStats },
 };
 
 int register_android_server_BatteryStatsService(JNIEnv *env)
@@ -850,8 +962,9 @@
             env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
     jclass clsPowerStateSubsystem =
             env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
+    jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
     if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
-            || clsPowerStateSubsystem == NULL) {
+            || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
         ALOGE("A rpmstats jni jclass is null.");
     } else {
         jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
@@ -862,6 +975,10 @@
                 "(Ljava/lang/String;JI)V");
         jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
                 "(Ljava/lang/String;JI)V");
+        jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
+                "(JLjava/lang/String;Ljava/lang/String;JJ)V");
+        jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
+                "(Z)V");
     }
 
     return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c18e98b..57377c6 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -249,7 +249,8 @@
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
-    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
+    virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+            uint32_t& policyFlags);
     virtual nsecs_t interceptKeyBeforeDispatching(
             const sp<IBinder>& token,
             const KeyEvent* keyEvent, uint32_t policyFlags);
@@ -1066,7 +1067,8 @@
     }
 }
 
-void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
+void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+        uint32_t& policyFlags) {
     ATRACE_CALL();
     // Policy:
     // - Ignore untrusted events and pass them along.
@@ -1084,7 +1086,7 @@
             JNIEnv* env = jniEnv();
             jint wmActions = env->CallIntMethod(mServiceObj,
                         gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
-                        when, policyFlags);
+                        displayId, when, policyFlags);
             if (checkAndClearExceptionFromCallback(env,
                     "interceptMotionBeforeQueueingNonInteractive")) {
                 wmActions = 0;
@@ -1794,7 +1796,7 @@
             "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
 
     GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
-            "interceptMotionBeforeQueueingNonInteractive", "(JI)I");
+            "interceptMotionBeforeQueueingNonInteractive", "(IJI)I");
 
     GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
             "interceptKeyBeforeDispatching",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index cbc3791..d178c3a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -973,7 +973,10 @@
         JavaObject& object) {
     translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object);
 
-    SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType)));
+    SET(CodeType, static_cast<int32_t>(measurement_V2_0->codeType));
+
+    // Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated.
+    SET(State, static_cast<int32_t>(measurement_V2_0->state));
 }
 
 jobject GnssMeasurementCallback::translateGnssClock(
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 9be728b..ec7a78b 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -295,6 +295,12 @@
     }
 }
 
+static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) {
+    bool retval = false;
+    getSuspendControl()->forceSuspend(&retval);
+    return retval;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gPowerManagerServiceMethods[] = {
@@ -303,6 +309,8 @@
             (void*) nativeInit },
     { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",
             (void*) nativeAcquireSuspendBlocker },
+    { "nativeForceSuspend", "()Z",
+            (void*) nativeForceSuspend },
     { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
             (void*) nativeReleaseSuspendBlocker },
     { "nativeSetInteractive", "(Z)V",
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
new file mode 100644
index 0000000..ab85a68
--- /dev/null
+++ b/services/devicepolicy/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "CtsDevicePolicyManagerTestCases"
+    }
+  ]
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index de5dd17..d5cfab9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -16,7 +16,7 @@
 
 package com.android.server.devicepolicy;
 
-import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
@@ -62,41 +62,43 @@
 
     private static Map<Integer, Integer> buildErrorCodesMap() {
         Map<Integer, Integer> map = new HashMap<>();
-        map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.ERROR,
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 DOWNLOAD_STATE_INITIALIZATION_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+                InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+                InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
 
         // Error constants corresponding to errors related to bad update file.
         map.put(
                 UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
 
         // Error constants corresponding to errors related to devices bad state.
         map.put(
                 UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
 
         return map;
     }
@@ -153,12 +155,13 @@
         } catch (ZipException e) {
             Log.w(UpdateInstaller.TAG, e);
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                     Log.getStackTraceString(e));
         } catch (IOException e) {
             Log.w(UpdateInstaller.TAG, e);
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
         }
     }
 
@@ -185,7 +188,7 @@
         if (mSizeForUpdate == -1) {
             Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                     "Failed to find payload entry in the given package.");
             return;
         }
@@ -210,7 +213,7 @@
                 if (entry.getMethod() != ZipEntry.STORED) {
                     Log.w(UpdateInstaller.TAG, "Invalid compression method.");
                     notifyCallbackOnError(
-                            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                            InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                             "Invalid compression method.");
                     return false;
                 }
@@ -263,7 +266,7 @@
             } else {
                 mUpdateInstaller.notifyCallbackOnError(
                         errorCodesMap.getOrDefault(
-                                errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+                                errorCode, InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN),
                         errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9523202..ae48dad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5181,7 +5181,7 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(
+            final ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
             if (ap.maximumTimeToUnlock != timeMs) {
                 ap.maximumTimeToUnlock = timeMs;
@@ -8388,7 +8388,7 @@
             return true;
         }
 
-        Log.w(LOG_TAG, String.format("Package if %s (uid=%d, pid=%d) cannot access Device IDs",
+        Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs",
                     packageName, uid, pid));
         return false;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
index 5f1e926..582306c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -16,7 +16,7 @@
 
 package com.android.server.devicepolicy;
 
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
@@ -45,7 +45,7 @@
         } catch (IOException e) {
             Log.w(TAG, "IO error while trying to install non AB update.", e);
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                     Log.getStackTraceString(e));
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
index cf68ccf..7148ed4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.content.Intent;
@@ -66,7 +66,7 @@
         mCopiedUpdateFile = null;
         if (!isBatteryLevelSufficient()) {
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
                     "The battery level must be above "
                             + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
                             + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
@@ -76,7 +76,7 @@
             mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
             if (mCopiedUpdateFile == null) {
                 notifyCallbackOnError(
-                        DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                        InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                         "Error while copying file.");
                 return;
             }
@@ -111,7 +111,7 @@
         } catch (IOException e) {
             Log.w(TAG, "Failed to copy update file to OTA directory", e);
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                     Log.getStackTraceString(e));
             return null;
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6017f2..aae159c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,6 +36,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -1350,9 +1351,7 @@
 
             traceBeginAndSlog("StartNetworkStack");
             try {
-                final android.net.NetworkStack networkStack =
-                        context.getSystemService(android.net.NetworkStack.class);
-                networkStack.start(context);
+                NetworkStackClient.getInstance().start(context);
             } catch (Throwable e) {
                 reportWtf("starting Network Stack", e);
             }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 638ec95..8ad4d76 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,10 @@
 java_library_static {
     name: "services.net",
     srcs: ["java/**/*.java"],
+    static_libs: [
+        "netd_aidl_interface-java",
+        "networkstack-aidl-interfaces-java",
+    ]
 }
 
 filegroup {
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
new file mode 100644
index 0000000..1eb7b98
--- /dev/null
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+public class NetworkStackClient {
+    private static final String TAG = NetworkStackClient.class.getSimpleName();
+
+    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
+    private static NetworkStackClient sInstance;
+
+    @NonNull
+    @GuardedBy("mPendingNetStackRequests")
+    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
+    @Nullable
+    @GuardedBy("mPendingNetStackRequests")
+    private INetworkStackConnector mConnector;
+
+    private volatile boolean mNetworkStackStartRequested = false;
+
+    private interface NetworkStackCallback {
+        void onNetworkStackConnected(INetworkStackConnector connector);
+    }
+
+    private NetworkStackClient() { }
+
+    /**
+     * Get the NetworkStackClient singleton instance.
+     */
+    public static synchronized NetworkStackClient getInstance() {
+        if (sInstance == null) {
+            sInstance = new NetworkStackClient();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create a DHCP server according to the specified parameters.
+     *
+     * <p>The server will be returned asynchronously through the provided callbacks.
+     */
+    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+            final IDhcpServerCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeDhcpServer(ifName, params, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create an IpClient on the specified interface.
+     *
+     * <p>The IpClient will be returned asynchronously through the provided callbacks.
+     */
+    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeIpClient(ifName, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create a NetworkMonitor.
+     *
+     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+     */
+    public void makeNetworkMonitor(
+            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeNetworkMonitor(network, name, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    private class NetworkStackConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            registerNetworkStackService(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // TODO: crash/reboot the system ?
+            Slog.wtf(TAG, "Lost network stack connector");
+        }
+    };
+
+    private void registerNetworkStackService(@NonNull IBinder service) {
+        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+        final ArrayList<NetworkStackCallback> requests;
+        synchronized (mPendingNetStackRequests) {
+            requests = new ArrayList<>(mPendingNetStackRequests);
+            mPendingNetStackRequests.clear();
+            mConnector = connector;
+        }
+
+        for (NetworkStackCallback r : requests) {
+            r.onNetworkStackConnected(connector);
+        }
+    }
+
+    /**
+     * Start the network stack. Should be called only once on device startup.
+     *
+     * <p>This method will start the network stack either in the network stack process, or inside
+     * the system server on devices that do not support the network stack module. The network stack
+     * connector will then be delivered asynchronously to clients that requested it before it was
+     * started.
+     */
+    public void start(Context context) {
+        mNetworkStackStartRequested = true;
+        // Try to bind in-process if the library is available
+        IBinder connector = null;
+        try {
+            final Class service = Class.forName(
+                    "com.android.server.NetworkStackService",
+                    true /* initialize */,
+                    context.getClassLoader());
+            connector = (IBinder) service.getMethod("makeConnector", Context.class)
+                    .invoke(null, context);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+            // TODO: crash/reboot system here ?
+            return;
+        } catch (ClassNotFoundException e) {
+            // Normal behavior if stack is provided by the app: fall through
+        }
+
+        // In-process network stack. Add the service to the service manager here.
+        if (connector != null) {
+            registerNetworkStackService(connector);
+            return;
+        }
+        // Start the network stack process. The service will be added to the service manager in
+        // NetworkStackConnection.onServiceConnected().
+        final Intent intent = new Intent(INetworkStackConnector.class.getName());
+        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+        intent.setComponent(comp);
+
+        if (comp == null) {
+            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+            // TODO: crash/reboot system server ?
+            return;
+        }
+        final PackageManager pm = context.getPackageManager();
+        int uid = -1;
+        try {
+            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf("Network stack package not found", e);
+            // Fall through
+        }
+        if (uid != Process.NETWORK_STACK_UID) {
+            throw new SecurityException("Invalid network stack UID: " + uid);
+        }
+
+        final int hasPermission =
+                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+        if (hasPermission != PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+        }
+
+        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            Slog.wtf(TAG,
+                    "Could not bind to network stack in-process, or in app with " + intent);
+            // TODO: crash/reboot system server if no network stack after a timeout ?
+        }
+    }
+
+    /**
+     * For non-system server clients, get the connector registered by the system server.
+     */
+    private INetworkStackConnector getRemoteConnector() {
+        // Block until the NetworkStack connector is registered in ServiceManager.
+        // <p>This is only useful for non-system processes that do not have a way to be notified of
+        // registration completion. Adding a callback system would be too heavy weight considering
+        // that the connector is registered on boot, so it is unlikely that a client would request
+        // it before it is registered.
+        // TODO: consider blocking boot on registration and simplify much of the logic in this class
+        IBinder connector;
+        try {
+            final long before = System.currentTimeMillis();
+            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+                Thread.sleep(20);
+                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+                    return null;
+                }
+            }
+        } catch (InterruptedException e) {
+            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+            return null;
+        }
+
+        return INetworkStackConnector.Stub.asInterface(connector);
+    }
+
+    private void requestConnector(@NonNull NetworkStackCallback request) {
+        // TODO: PID check.
+        final int caller = Binder.getCallingUid();
+        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
+            // Don't even attempt to obtain the connector and give a nice error message
+            throw new SecurityException(
+                    "Only the system server should try to bind to the network stack.");
+        }
+
+        if (!mNetworkStackStartRequested) {
+            // The network stack is not being started in this process, e.g. this process is not
+            // the system server. Get a remote connector registered by the system server.
+            final INetworkStackConnector connector = getRemoteConnector();
+            synchronized (mPendingNetStackRequests) {
+                mConnector = connector;
+            }
+            request.onNetworkStackConnected(connector);
+            return;
+        }
+
+        final INetworkStackConnector connector;
+        synchronized (mPendingNetStackRequests) {
+            connector = mConnector;
+            if (connector == null) {
+                mPendingNetStackRequests.add(request);
+                return;
+            }
+        }
+
+        request.onNetworkStackConnected(connector);
+    }
+}
diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
similarity index 100%
rename from core/java/android/net/dhcp/DhcpServerCallbacks.java
rename to services/net/java/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
similarity index 100%
rename from core/java/android/net/ip/IpClientCallbacks.java
rename to services/net/java/android/net/ip/IpClientCallbacks.java
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 2a2a67a..bf917bf 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -23,8 +23,7 @@
 import android.net.DhcpResultsParcelable;
 import android.net.LinkProperties;
 import android.net.LinkPropertiesParcelable;
-import android.net.NetworkStack;
-import android.net.ip.IIpClientCallbacks;
+import android.net.NetworkStackClient;
 import android.os.ConditionVariable;
 
 import java.io.FileDescriptor;
@@ -76,30 +75,17 @@
      *
      * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
      * {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+     * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
      */
     public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
-    }
-
-    /**
-     * Create a new IpClient.
-     *
-     * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
-     * instead of {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
-     */
-    public static void makeIpClient(
-            Context context, String ifName, IpClientCallbacksProxy callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, callback);
+        // TODO: migrate clients and remove context argument
+        NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
     }
 
     /**
      * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
      */
-    public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+    private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
         protected final IpClientCallbacks mCb;
 
         /**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..34fc735 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -22,7 +22,6 @@
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 
-import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
@@ -31,7 +30,7 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -132,10 +131,6 @@
     }
 
     public static class Dependencies {
-        private final Context mContext;
-        public Dependencies(Context context) {
-            mContext = context;
-        }
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
@@ -153,7 +148,7 @@
          */
         public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb) {
-            mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
+            NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
         }
     }
 
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml
deleted file mode 100644
index 0f4e8a3..0000000
--- a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<certificates>
-  <metadata>
-    <serial>
-      1000
-    </serial>
-    <creation-time>
-      1515697631
-    </creation-time>
-    <refresh-interval>
-      2592000
-    </refresh-interval>
-    <refresh-interval>
-      2592000
-    </refresh-interval>
-    <previous>
-      <serial>
-        0
-      </serial>
-      <hash>
-        47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
-      </hash>
-    </previous>
-  </metadata>
-  <endpoints>
-    <cert>
-      MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
-      R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
-      NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
-      YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
-      tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
-      4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
-      tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
-      HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
-      GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
-      UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
-      33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
-      7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
-      hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
-      79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
-      M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
-      JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
-      BuwwuQxvQDF4pmQd
-    </cert>
-  </endpoints>
-</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml
similarity index 100%
rename from services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml
rename to services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
index 4bac200..ebbebcb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
@@ -16,12 +16,12 @@
 
 package com.android.server;
 
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 import static android.util.DebugUtils.valueToString;
 
 import static org.junit.Assert.assertEquals;
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 bd03a8d..04abeca1 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -403,7 +403,8 @@
         protected int broadcastIntent(Intent intent, String resolvedType,
                 IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
                 String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered,
-                boolean sticky, int callingPid, int callingUid, int userId) {
+                boolean sticky, int callingPid, int callingUid, int realCallingUid,
+                int realCallingPid, int userId) {
             Log.i(TAG, "broadcastIntentLocked " + intent);
             mSentIntents.add(intent);
             return 0;
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index a3f36b7..3e5ce46 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -57,6 +57,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -125,6 +127,7 @@
     private File mTestDir;
     private File mSuppressFile;
     private File mActivatedFile;
+    private File mRememberActivatedFile;
 
     @Before
     public void setUp() throws Exception {
@@ -153,6 +156,8 @@
 
         mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
         TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
+        mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM);
+        TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile);
 
         mTrampoline = new TrampolineTestable(mContextMock);
     }
@@ -411,6 +416,34 @@
     }
 
     @Test
+    public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false));
+    }
+
+    @Test
+    public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+    }
+
+    @Test
+    public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+    }
+
+    @Test
     public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.dataChanged(PACKAGE_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1291,6 +1324,7 @@
         static BackupManagerService sBackupManagerServiceMock = null;
         static File sSuppressFile = null;
         static SparseArray<File> sActivatedFiles = new SparseArray<>();
+        static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
         static UserManager sUserManagerMock = null;
         private int mCreateServiceCallsCount = 0;
 
@@ -1314,6 +1348,11 @@
         }
 
         @Override
+        protected File getRememberActivatedFileForNonSystemUser(int userId) {
+            return sRememberActivatedFiles.get(userId);
+        }
+
+        @Override
         protected File getActivatedFileForNonSystemUser(int userId) {
             return sActivatedFiles.get(userId);
         }
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 0355e84..5cb6cbb 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -556,16 +556,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) throws RemoteException {
-        return new byte[0];
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException {
-
-    }
-
-    @Override
     public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
         throws RemoteException {
         return null;
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
new file mode 100644
index 0000000..eaa9c45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 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.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.io.Files;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
+    private static File sTemporaryDir;
+    private File mTemporaryFile;
+
+    @BeforeClass
+    public static void setUpClass() {
+        sTemporaryDir = Files.createTempDir();
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        if (sTemporaryDir != null) {
+            sTemporaryDir.delete();
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt");
+    }
+
+    /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */
+    @Test
+    public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() {
+        assertThat(!mTemporaryFile.exists());
+
+        FileUtils.createNewFile(mTemporaryFile);
+
+        assertThat(mTemporaryFile.exists());
+    }
+
+    /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */
+    @Test
+    public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException {
+        mTemporaryFile.createNewFile();
+
+        FileUtils.createNewFile(mTemporaryFile);
+
+        assertThat(mTemporaryFile.exists());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
new file mode 100644
index 0000000..ca699bd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 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.backup.utils;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RandomAccessFileUtilsTest {
+    private File mTemporaryFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mTemporaryFile = File.createTempFile("fileutilstest", ".txt");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTemporaryFile != null) {
+            mTemporaryFile.delete();
+        }
+    }
+
+    /**
+     * Test that if we write true, we read back true.
+     */
+    @Test
+    public void testWriteTrue_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+
+    /**
+     * Test that if we write false, we read back false.
+     */
+    @Test
+    public void testWriteFalse_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write true twice, we read back true.
+     */
+    @Test
+    public void testWriteTrueTwice_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+
+    /**
+     * Test that if we write false twice, we read back false.
+     */
+    @Test
+    public void testWriteFalseTwice_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write true and then false, we read back false.
+     */
+    @Test
+    public void testWriteTrueFalse_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write false and then true, we read back true.
+     */
+    @Test
+    public void testWriteFalseTrue_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 5900fc5..01759d2 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -98,6 +98,8 @@
 
         mColorDisplayService = new ColorDisplayService(mContext);
         mBinderService = mColorDisplayService.new BinderService();
+        LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+                        mColorDisplayService.new ColorDisplayServiceInternal());
     }
 
     @After
@@ -110,6 +112,8 @@
 
         mUserId = UserHandle.USER_NULL;
         mContext = null;
+
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
     @AfterClass
@@ -979,6 +983,99 @@
         assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
     }
 
+    @Test
+    public void displayWhiteBalance_enable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+        startService();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_disableAfterNightDisplayEnable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        /* Since we are using FakeSettingsProvider which could not trigger observer change,
+         * force an update here.*/
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterNightDisplayDisable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+
+        /* Disable nightlight */
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterLinearColorMode() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        startService();
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureOverMax() {
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(max);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureBelowMin() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(min);
+    }
+
+    @Test
+    public void displayWhiteBalance_setValidTemperature() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+        int valToSet = (min + max) / 2;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(valToSet);
+    }
+
     /**
      * Configures Night display to use a custom schedule.
      *
@@ -1041,6 +1138,16 @@
     }
 
     /**
+     * Configures the Display White Balance setting state.
+     *
+     * @param state {@code true} if display white balance should be enabled
+     */
+    private void setWhiteBalance(boolean state) {
+        Secure.putIntForUser(mContext.getContentResolver(),
+                Secure.DISPLAY_WHITE_BALANCE_ENABLED, state ? 1 : 0, mUserId);
+    }
+
+    /**
      * Configures color mode.
      */
     private void setColorMode(int colorMode) {
@@ -1111,6 +1218,17 @@
     }
 
     /**
+     * Convenience method for asserting that the DWB active status matches expectation.
+     *
+     * @param enabled the expected active status.
+     */
+    private void assertDwbActive(boolean enabled) {
+        assertWithMessage("Incorrect Display White Balance state")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated())
+                .isEqualTo(enabled);
+    }
+
+    /**
      * Convenience for making a {@link LocalTime} instance with an offset relative to now.
      *
      * @param offsetMinutes the offset relative to now (in minutes)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
index bbcc411..9836c64 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
@@ -47,7 +47,6 @@
     public void parse_succeeds() throws Exception {
         CertXml certXml = CertXml.parse(certXmlBytes);
         assertThat(certXml.getSerial()).isEqualTo(1000L);
-        assertThat(certXml.getRefreshInterval()).isEqualTo(2592000L);
     }
 
     @Test
@@ -75,6 +74,13 @@
     }
 
     @Test
+    public void parse_doesNotThrowIfNoRefreshInterval() throws Exception {
+        CertXml.parse(
+                TestData.readTestFile(
+                        "xml/valid-cert-file-no-refresh-interval.xml"));
+    }
+
+    @Test
     public void parse_throwsIfNoEndpointCert() throws Exception {
         CertParsingException expected =
                 expectThrows(
@@ -87,18 +93,6 @@
     }
 
     @Test
-    public void parse_throwsIfNoRefreshInterval() throws Exception {
-        CertParsingException expected =
-                expectThrows(
-                        CertParsingException.class,
-                        () ->
-                                CertXml.parse(
-                                        TestData.readTestFile(
-                                                "xml/invalid-cert-file-no-refresh-interval.xml")));
-        assertThat(expected.getMessage()).contains("exactly one");
-    }
-
-    @Test
     public void parse_throwsIfNoSerial() throws Exception {
         CertParsingException expected =
                 expectThrows(
@@ -111,19 +105,6 @@
     }
 
     @Test
-    public void parse_throwsIfTwoRefreshIntervals() throws Exception {
-        CertParsingException expected =
-                expectThrows(
-                        CertParsingException.class,
-                        () ->
-                                CertXml.parse(
-                                        TestData.readTestFile(
-                                                "xml/invalid-cert-file-two-refresh-intervals"
-                                                        + ".xml")));
-        assertThat(expected.getMessage()).contains("exactly one");
-    }
-
-    @Test
     public void parse_throwsIfTwoSerials() throws Exception {
         CertParsingException expected =
                 expectThrows(
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6d28ed1..50734ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -55,8 +55,8 @@
 
 import com.android.internal.os.AtomicFile;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -88,7 +88,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -103,7 +104,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -120,7 +122,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -143,7 +146,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -313,7 +317,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -507,7 +512,8 @@
     public void testUpdatePackageSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
@@ -625,7 +631,8 @@
     public void testCreateNewSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 8d0365b..9504381 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -32,6 +32,7 @@
 
 import android.Manifest.permission;
 import android.app.ActivityManager;
+import android.app.Person;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
@@ -890,6 +891,7 @@
                 .setText("text")
                 .setDisabledMessage("dismes")
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+                .setPerson(makePerson("person", "personKey", "personUri"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setRank(123)
                 .setExtras(pb)
@@ -901,6 +903,8 @@
                 .setTitle("x")
                 .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setPersons(list(makePerson("person1", "personKey1", "personUri1"),
+                        makePerson("person2", "personKey2", "personUri2")).toArray(new Person[2]))
                 .setRank(456)
                 .build();
         sorig2.setTimestamp(mInjectedCurrentTimeMillis);
@@ -936,6 +940,10 @@
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(1, si.getPersons().length);
+        assertEquals("person", si.getPersons()[0].getName());
+        assertEquals("personKey", si.getPersons()[0].getKey());
+        assertEquals("personUri", si.getPersons()[0].getUri());
         assertEquals(0, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
@@ -949,6 +957,8 @@
         // to test it.
         si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
         assertEquals(1, si.getRank());
+        assertEquals(2, si.getPersons().length);
+        assertEquals("personUri2", si.getPersons()[1].getUri());
 
         dumpUserFile(USER_10);
     }
@@ -1114,6 +1124,7 @@
                 .setDisabledMessage("dismes")
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setPerson(makePerson("person", "personKey", "personUri"))
                 .setRank(123)
                 .setExtras(pb)
                 .build();
@@ -1150,6 +1161,7 @@
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(0, si.getPersons().length); // Don't backup the persons field
         assertEquals(0, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 48ab8d6..0196279 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -568,7 +568,8 @@
     }
 
     private PackageDynamicCode getPackageDynamicCodeInfo(TestData testData) {
-        return mDexManager.getDexLogger().getPackageDynamicCodeInfo(testData.getPackageName());
+        return mDexManager.getDynamicCodeLogger()
+                .getPackageDynamicCodeInfo(testData.getPackageName());
     }
 
     private void assertNoUseInfo(TestData testData) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
similarity index 85%
rename from services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
rename to services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index 6da202b..7992ba3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -53,7 +53,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class DexLoggerTests {
+public class DynamicCodeLoggerTests {
     private static final String OWNING_PACKAGE_NAME = "package.name";
     private static final String VOLUME_UUID = "volUuid";
     private static final String FILE_PATH = "/bar/foo.jar";
@@ -85,7 +85,7 @@
     @Mock IPackageManager mPM;
     @Mock Installer mInstaller;
 
-    private DexLogger mDexLogger;
+    private DynamicCodeLogger mDynamicCodeLogger;
 
     private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create();
     private boolean mWriteTriggered = false;
@@ -106,7 +106,7 @@
         };
 
         // For test purposes capture log messages as well as sending to the event log.
-        mDexLogger = new DexLogger(mPM, mInstaller, packageDynamicCodeLoading) {
+        mDynamicCodeLogger = new DynamicCodeLogger(mPM, mInstaller, packageDynamicCodeLoading) {
             @Override
             void writeDclEvent(String subtag, int uid, String message) {
                 super.writeDclEvent(subtag, uid, message);
@@ -131,13 +131,13 @@
         whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
 
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
 
         assertThat(mWriteTriggered).isFalse();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading())
                 .containsExactly(OWNING_PACKAGE_NAME);
     }
 
@@ -146,14 +146,14 @@
         whenFileIsHashed(FILE_PATH, doReturn(EMPTY_BYTES));
 
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
 
         // File should be removed from the DCL list, since we can't hash it.
         assertThat(mWriteTriggered).isTrue();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     @Test
@@ -162,24 +162,24 @@
                 doThrow(new InstallerException("Intentional failure for test")));
 
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
 
         // File should be removed from the DCL list, since we can't hash it.
         assertThat(mWriteTriggered).isTrue();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     @Test
     public void testOneLoader_ownFile_unknownPath() {
         recordLoad(OWNING_PACKAGE_NAME, "other/path");
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isTrue();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     @Test
@@ -189,7 +189,7 @@
         setPackageUid(OWNING_PACKAGE_NAME, -1);
 
         recordLoad(OWNING_PACKAGE_NAME, filePath);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
     }
@@ -200,7 +200,7 @@
         setPackageUid("other.package.name", 1001);
 
         recordLoad("other.package.name", FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(1001);
         assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
@@ -213,7 +213,7 @@
         setPackageUid("other.package.name", -1);
 
         recordLoad("other.package.name", FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isFalse();
@@ -224,14 +224,14 @@
         whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
 
         recordLoadNative(FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
         assertThat(mMessagesForUid)
                 .containsEntry(OWNER_UID, EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH);
 
         assertThat(mWriteTriggered).isFalse();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading())
                 .containsExactly(OWNING_PACKAGE_NAME);
     }
 
@@ -247,7 +247,7 @@
         recordLoad("other.package.name1", otherDexPath);
         recordLoad("other.package.name2", FILE_PATH);
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
@@ -256,10 +256,10 @@
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
 
         assertThat(mWriteTriggered).isTrue();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading())
                 .containsExactly(OWNING_PACKAGE_NAME);
 
-        // Check the DexLogger caching is working
+        // Check the DynamicCodeLogger caching is working
         verify(mPM, atMost(1)).getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID);
     }
 
@@ -267,7 +267,7 @@
     public void testUnknownOwner() {
         reset(mPM);
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading("other.package.name");
+        mDynamicCodeLogger.logDynamicCodeLoading("other.package.name");
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isFalse();
@@ -278,11 +278,11 @@
     public void testUninstalledPackage() {
         reset(mPM);
         recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
-        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+        mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isTrue();
-        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+        assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     private void setPackageUid(String packageName, int uid) throws Exception {
@@ -295,7 +295,8 @@
     }
 
     private void recordLoad(String loadingPackageName, String dexPath) {
-        mDexLogger.recordDex(OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName);
+        mDynamicCodeLogger.recordDex(
+                OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName);
         mWriteTriggered = false;
     }
 
@@ -304,7 +305,7 @@
         String[] packageNames = { OWNING_PACKAGE_NAME };
         when(mPM.getPackagesForUid(loadingUid)).thenReturn(packageNames);
 
-        mDexLogger.recordNative(loadingUid, nativePath);
+        mDynamicCodeLogger.recordNative(loadingUid, nativePath);
         mWriteTriggered = false;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 9f1cbcd..6a937fa 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -98,6 +98,15 @@
     }
 
     @Test
+    public void testUpdateUserActivity_schedulesTheNextCheck() {
+        long now = SystemClock.uptimeMillis();
+        mNextDimming = now;
+        mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
+        long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L);
+        assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L);
+    }
+
+    @Test
     public void testOnUserActivity_ignoresAfterMaximumExtension() {
         long now = SystemClock.uptimeMillis();
         mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 63341b6..911c4a2 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,15 +21,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManagerInternal;
+import android.attention.AttentionManagerInternal;
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.BatteryManagerInternal;
+import android.os.Binder;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
@@ -53,6 +59,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Tests for {@link com.android.server.power.PowerManagerService}
  */
@@ -67,6 +76,7 @@
     private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
     private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
     private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+    private @Mock AttentionManagerInternal mAttentionManagerInternalMock;
     private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
     private @Mock Notifier mNotifierMock;
     private PowerManagerService mService;
@@ -93,6 +103,7 @@
         addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
         addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
         addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+        addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
 
         mService = new PowerManagerService(getContext(), new Injector() {
             Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
@@ -210,4 +221,80 @@
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
     }
+
+    @SmallTest
+    public void testForceSuspend_putsDeviceToSleep() {
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Verify that we start awake
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Grab the wakefulness value when PowerManager finally calls into the
+        // native component to actually perform the suspend.
+        when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> {
+            assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+            return true;
+        });
+
+        boolean retval = mService.getBinderServiceInstance().forceSuspend();
+        assertThat(retval).isTrue();
+
+        // Still asleep when the function returns.
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+    }
+
+    @SmallTest
+    public void testForceSuspend_pakeLocksDisabled() {
+        final String tag = "TestWakelockTag_098213";
+        final int flags = PowerManager.PARTIAL_WAKE_LOCK;
+        final String pkg = getContext().getOpPackageName();
+
+        // Set up the Notification mock to keep track of the wakelocks that are currently
+        // active or disabled. We'll use this to verify that wakelocks are disabled when
+        // they should be.
+        final Map<String, Integer> wakelockMap = new HashMap<>(1);
+        doAnswer(inv -> {
+            wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]);
+            return null;
+        }).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(),
+                anyInt(), any(), any());
+        doAnswer(inv -> {
+            wakelockMap.remove((String) inv.getArguments()[1]);
+            return null;
+        }).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(),
+                anyInt(), any(), any());
+
+        //
+        // TEST STARTS HERE
+        //
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Verify that we start awake
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Create a wakelock
+        mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg,
+                null /* workSource */, null /* historyTag */);
+        assertThat(wakelockMap.get(tag)).isEqualTo(flags);  // Verify wakelock is active.
+
+        // Confirm that the wakelocks have been disabled when the forceSuspend is in flight.
+        when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> {
+            // Verify that the wakelock is disabled by the time we get to the native force
+            // suspend call.
+            assertThat(wakelockMap.containsKey(tag)).isFalse();
+            return true;
+        });
+
+        assertThat(mService.getBinderServiceInstance().forceSuspend()).isTrue();
+        assertThat(wakelockMap.get(tag)).isEqualTo(flags);
+
+    }
+
+    @SmallTest
+    public void testForceSuspend_forceSuspendFailurePropogated() {
+        when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false);
+        assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8c36905..a1db3e8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -27,9 +27,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -158,35 +155,6 @@
         assertTrue(mActivity.isState(STOPPED));
     }
 
-    @Test
-    public void testPositionLimitedAspectRatioNavBarBottom() {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f,
-                new Rect(0, 0, 1000, 1500));
-    }
-
-    @Test
-    public void testPositionLimitedAspectRatioNavBarLeft() {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f,
-                new Rect(500, 0, 2000, 1000));
-    }
-
-    @Test
-    public void testPositionLimitedAspectRatioNavBarRight() {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f,
-                new Rect(0, 0, 1500, 1000));
-    }
-
-    private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
-            float aspectRatio, Rect expectedActivityBounds) {
-        // Verify with nav bar on the right.
-        when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId()))
-                .thenReturn(navBarPosition);
-        mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
-        mActivity.info.maxAspectRatio = aspectRatio;
-        ensureActivityConfiguration();
-        assertEquals(expectedActivityBounds, mActivity.getBounds());
-    }
-
     private void ensureActivityConfiguration() {
         mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index a7520dc..2627ec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -30,6 +30,7 @@
 
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -48,6 +49,7 @@
  *  atest WmTests:AppChangeTransitionTests
  */
 @SmallTest
+@Presubmit
 public class AppChangeTransitionTests extends WindowTestsBase {
 
     private TaskStack mStack;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index cd13209..1dd72ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,7 +51,6 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -142,7 +141,6 @@
         mToken.removeImmediately();
     }
 
-    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     @Test
     public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
@@ -303,7 +301,6 @@
     }
 
     @Test
-    @FlakyTest(detail = "Promote once confirmed non-flaky")
     public void testStuckExitingWindow() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
@@ -346,7 +343,6 @@
         assertNoStartingWindow(mToken);
     }
 
-    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     @Test
     public void testAddRemoveRace() {
         // There was once a race condition between adding and removing starting windows
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 3f83cae..1e02a12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 import android.view.IWindowManager;
 
@@ -71,6 +72,7 @@
  *  atest WmTests:AssistDataRequesterTest
  */
 @MediumTest
+@Presubmit
 public class AssistDataRequesterTest extends ActivityTestsBase {
 
     private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index a62bc71..2452ef0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -61,7 +61,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
-import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -87,18 +86,22 @@
  * Tests for the {@link DisplayContent} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:DisplayContentTests
+ *  atest WmTests:DisplayContentTests
  */
 @SmallTest
 @Presubmit
 public class DisplayContentTests extends WindowTestsBase {
 
     @Test
-    @FlakyTest(bugId = 77772044)
+    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     public void testForAllWindows() {
         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 mDisplayContent, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
+        // Wait until everything in animation handler get executed to prevent the exiting window
+        // from being removed during WindowSurfacePlacer Traversal.
+        waitUntilHandlersIdle();
+
         exitingAppToken.mIsExiting = true;
         exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 198e7ce..b15e99a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -60,6 +60,7 @@
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.After;
 import org.junit.Before;
@@ -113,6 +114,7 @@
     public static void setUpOnce() {
         sMockWm = mock(WindowManagerService.class);
         sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+        sMockWm.mPolicy = mock(WindowManagerPolicy.class);
     }
 
     @Before
@@ -807,6 +809,8 @@
 
             mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
             mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+            when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
+                    .thenReturn(WmDisplayCutout.NO_CUTOUT);
 
             mMockDisplayPolicy = mock(DisplayPolicy.class);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index bc62e8c..2d906d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,6 +19,7 @@
 import static android.view.InsetsState.TYPE_IME;
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
@@ -28,15 +29,33 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.ViewRootImpl;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 @SmallTest
 @Presubmit
 public class InsetsStateControllerTest extends WindowTestsBase {
+    private static int sPreviousNewInsetsMode;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        // TODO: Make use of SettingsSession when it becomes feasible for this.
+        sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode;
+        // To let the insets provider control the insets visibility, the insets mode has to be
+        // NEW_INSETS_MODE_FULL.
+        ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode;
+    }
 
     @Test
     public void testStripForDispatch_notOwn() {
@@ -47,7 +66,7 @@
         assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
     }
 
-    @FlakyTest(bugId = 69229402)
+    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testStripForDispatch_own() {
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
@@ -57,7 +76,7 @@
         assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
     }
 
-    @FlakyTest(bugId = 124088319)
+    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testStripForDispatch_navBar() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
@@ -69,7 +88,7 @@
         assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
     }
 
-    @FlakyTest(bugId = 124088319)
+    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testBarControllingWinChanged() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
@@ -82,7 +101,7 @@
         assertEquals(2, controls.length);
     }
 
-    @FlakyTest(bugId = 124088319)
+    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testControlRevoked() {
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
index ce22788..df26679 100644
--- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
@@ -34,6 +34,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
 import android.util.SparseBooleanArray;
 
 import com.android.server.wm.LockTaskController.LockTaskToken;
@@ -43,6 +44,7 @@
 
 import java.lang.reflect.Constructor;
 
+@Presubmit
 public class KeyguardDisableHandlerTest {
 
     private KeyguardDisableHandler mKeyguardDisable;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index cc6a58a..763ea62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -112,8 +112,8 @@
         }
     }
 
-    @FlakyTest(bugId = 117117823)
     @Test
+    @FlakyTest(bugId = 117117823)
     public void testIncludedApps_expectTargetAndVisible() {
         mWm.setRecentsAnimationController(mController);
         final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index c595868..2377df4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -34,10 +34,12 @@
 
 import android.app.IActivityTaskManager;
 import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -50,6 +52,8 @@
  *  atest WmTests:TaskPositionerTests
  */
 @SmallTest
+@Presubmit
+@FlakyTest
 public class TaskPositionerTests extends WindowTestsBase {
 
     private static final boolean DEBUGGING = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index bcf9dd2..388f98f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -24,6 +24,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -307,6 +308,41 @@
         assertEquals(fullScreenBounds, task.getBounds());
     }
 
+    @Test
+    public void testComputeConfigResourceOverrides() {
+        final TaskRecord task = new TaskBuilder(mSupervisor).build();
+        final Configuration inOutConfig = new Configuration();
+        final Configuration parentConfig = new Configuration();
+        final int longSide = 1200;
+        final int shortSide = 600;
+        parentConfig.densityDpi = 400;
+        parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
+        parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
+
+        // Portrait bounds.
+        inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide);
+        // By default, the parent bounds should limit the existing input bounds.
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
+        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
+
+        inOutConfig.setToDefaults();
+        // Landscape bounds.
+        inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide);
+        // Without limiting to be inside the parent bounds, the out screen size should keep relative
+        // to the input bounds.
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig,
+                false /* insideParentBounds */);
+
+        assertEquals(shortSide * DENSITY_DEFAULT / parentConfig.densityDpi,
+                inOutConfig.screenHeightDp);
+        assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
+                inOutConfig.screenWidthDp);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
+    }
+
     /** Ensures that the alias intent won't have target component resolved. */
     @Test
     public void testTaskIntentActivityAlias() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index dcca316..42a205a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -100,7 +100,7 @@
     }
 
     @Test
-    @FlakyTest(bugId = 119893767)
+    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     public void testTaskDescriptionChanged() throws Exception {
         final Object[] params = new Object[2];
         final CountDownLatch latch = new CountDownLatch(1);
@@ -122,14 +122,18 @@
                 }
             }
         });
-        final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class);
+
+        int taskId;
+        synchronized (sLock) {
+            taskId = startTestActivity(ActivityTaskDescriptionChange.class).getTaskId();
+        }
         waitForCallback(latch);
-        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals(taskId, params[0]);
         assertEquals("Test Label", ((TaskDescription) params[1]).getLabel());
     }
 
     @Test
-    @FlakyTest(bugId = 119893767)
+    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     public void testActivityRequestedOrientationChanged() throws Exception {
         final int[] params = new int[2];
         final CountDownLatch latch = new CountDownLatch(1);
@@ -142,9 +146,12 @@
                 latch.countDown();
             }
         });
-        final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class);
+        int taskId;
+        synchronized (sLock) {
+            taskId = startTestActivity(ActivityRequestedOrientationChange.class).getTaskId();
+        }
         waitForCallback(latch);
-        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals(taskId, params[0]);
         assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]);
     }
 
@@ -152,7 +159,7 @@
      * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted.
      */
     @Test
-    @FlakyTest(bugId = 119893767)
+    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     public void testTaskChangeCallBacks() throws Exception {
         final Object[] params = new Object[2];
         final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
@@ -245,7 +252,7 @@
 
     private void waitForCallback(CountDownLatch latch) {
         try {
-            final boolean result = latch.await(2, TimeUnit.SECONDS);
+            final boolean result = latch.await(4, TimeUnit.SECONDS);
             if (!result) {
                 throw new RuntimeException("Timed out waiting for task stack change notification");
             }
@@ -324,7 +331,11 @@
         protected void onPostResume() {
             super.onPostResume();
             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-            finish();
+            synchronized (sLock) {
+                // Hold the lock to ensure no one is trying to access fields of this Activity in
+                // this test.
+                finish();
+            }
         }
     }
 
@@ -333,7 +344,11 @@
         protected void onPostResume() {
             super.onPostResume();
             setTaskDescription(new TaskDescription("Test Label"));
-            finish();
+            synchronized (sLock) {
+                // Hold the lock to ensure no one is trying to access fields of this Activity in
+                // this test.
+                finish();
+            }
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index bfb9193..849772a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -156,7 +156,8 @@
     }
 
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
+            int policyFlags) {
         return 0;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3eb9085..b0e20b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -35,6 +35,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -54,7 +55,6 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
 
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -87,6 +87,7 @@
  */
 @SmallTest
 @Presubmit
+@FlakyTest(bugId = 124127512)
 public class WindowStateTests extends WindowTestsBase {
     private static int sPreviousNewInsetsMode;
 
@@ -389,6 +390,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 74078662)
     public void testLayoutSeqResetOnReparent() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mLayoutSeq = 1;
@@ -445,6 +447,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 74078662)
     public void testDisplayCutoutIsCalculatedRelativeToFrame() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         WindowFrames wf = app.getWindowFrames();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
index 2b8e307..b299f0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -36,11 +36,10 @@
 import org.junit.Test;
 
 import java.io.File;
-import java.io.IOException;
 
 
 /**
- * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ * Test class for {@link WindowTraceBuffer}.
  *
  * Build/Install/Run:
  *  atest WmTests:WindowTraceBufferTest
@@ -49,12 +48,15 @@
 @Presubmit
 public class WindowTraceBufferTest {
     private File mFile;
+    private WindowTraceBuffer mBuffer;
 
     @Before
     public void setUp() throws Exception {
         final Context testContext = getInstrumentation().getContext();
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
+
+        mBuffer = new WindowTraceBuffer(10);
     }
 
     @After
@@ -63,145 +65,112 @@
     }
 
     @Test
-    public void testTraceQueueBuffer_addItem() throws Exception {
-        ProtoOutputStream toWrite1 = getDummy(1);
-        ProtoOutputStream toWrite2 = getDummy(2);
-        ProtoOutputStream toWrite3 = getDummy(3);
-        final int objectSize = toWrite1.getRawSize();
-        final int bufferCapacity = objectSize * 2;
-
-        final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
-
-        buffer.add(toWrite1);
-        byte[] toWrite1Bytes = toWrite1.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-
-        buffer.add(toWrite2);
-        byte[] toWrite2Bytes = toWrite2.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-
-        buffer.add(toWrite3);
-        byte[] toWrite3Bytes = toWrite3.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-        assertTrue("Third element should not be in the list",
-                !buffer.contains(toWrite3Bytes));
-
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
-        assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals("Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
-    }
-
-    @Test
-    public void testTraceRingBuffer_addItem() throws Exception {
+    public void test_addItem() {
         ProtoOutputStream toWrite = getDummy(1);
         final int objectSize = toWrite.getRawSize();
+        mBuffer.setCapacity(objectSize);
+        mBuffer.resetBuffer();
 
-        final WindowTraceBuffer buffer = buildRingBuffer(objectSize);
+        Preconditions.checkArgument(mBuffer.size() == 0);
 
-        Preconditions.checkArgument(buffer.mBuffer.isEmpty());
+        mBuffer.add(toWrite);
 
-        buffer.add(toWrite);
-
-        assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
         assertEquals("Total buffer getSize differs from inserted object",
-                buffer.mBufferSize, objectSize);
-        assertEquals("Available buffer space does not match used one",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), objectSize);
+        assertEquals("Available buffer space does not match used one", 0,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception {
+    public void test_addItemMustOverwriteOne() {
         ProtoOutputStream toWrite1 = getDummy(1);
         ProtoOutputStream toWrite2 = getDummy(2);
         ProtoOutputStream toWrite3 = getDummy(3);
         final int objectSize = toWrite1.getRawSize();
-
         final int bufferCapacity = objectSize * 2 + 1;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
-        buffer.add(toWrite1);
+        mBuffer.add(toWrite1);
         byte[] toWrite1Bytes = toWrite1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
 
-        buffer.add(toWrite2);
+        mBuffer.add(toWrite2);
         byte[] toWrite2Bytes = toWrite2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
 
-        buffer.add(toWrite3);
+        mBuffer.add(toWrite3);
         byte[] toWrite3Bytes = toWrite3.getBytes();
         assertTrue("First element should not be in the list",
-                !buffer.contains(toWrite1Bytes));
+                !mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
         assertTrue("Third element should be in the list",
-                buffer.contains(toWrite3Bytes));
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+                mBuffer.contains(toWrite3Bytes));
+        assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
         assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity - 1);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 1);
+                mBuffer.getBufferSize(), bufferCapacity - 1);
+        assertEquals(" Buffer is full, available space should be 0", 1,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception {
+    public void test_addItemMustOverwriteMultiple() {
         ProtoOutputStream toWriteSmall1 = getDummy(1);
         ProtoOutputStream toWriteSmall2 = getDummy(2);
         final int objectSize = toWriteSmall1.getRawSize();
-
         final int bufferCapacity = objectSize * 2;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
         ProtoOutputStream toWriteBig = new ProtoOutputStream();
         toWriteBig.write(MAGIC_NUMBER, 1);
         toWriteBig.write(MAGIC_NUMBER, 2);
 
-        buffer.add(toWriteSmall1);
+        mBuffer.add(toWriteSmall1);
         byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
 
-        buffer.add(toWriteSmall2);
+        mBuffer.add(toWriteSmall2);
         byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWriteSmall2Bytes));
+                mBuffer.contains(toWriteSmall2Bytes));
 
-        buffer.add(toWriteBig);
+        mBuffer.add(toWriteBig);
         byte[] toWriteBigBytes = toWriteBig.getBytes();
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall1Bytes));
+                !mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall2Bytes));
+                !mBuffer.contains(toWriteSmall2Bytes));
         assertTrue("Third element should overwrite all others",
-                buffer.contains(toWriteBigBytes));
+                mBuffer.contains(toWriteBigBytes));
 
-        assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1);
+        assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
         assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), bufferCapacity);
+        assertEquals(" Buffer is full, available space should be 0", 0,
+                mBuffer.getAvailableSpace());
     }
 
-    private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException {
-        return new WindowTraceBuffer.Builder()
-                .setContinuousMode(true)
-                .setBufferCapacity(capacity)
-                .setTraceFile(mFile)
-                .build();
+    @Test
+    public void test_startResetsBuffer() {
+        ProtoOutputStream toWrite = getDummy(1);
+        mBuffer.resetBuffer();
+        Preconditions.checkArgument(mBuffer.size() == 0);
+
+        mBuffer.add(toWrite);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+        mBuffer.resetBuffer();
+        assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
+        assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
     }
 
     private ProtoOutputStream getDummy(int value) {
@@ -212,7 +181,4 @@
         return toWrite;
     }
 
-    private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
-        return new WindowTraceQueueBuffer(size, mFile, false);
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 3c6e240..8358fdd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -88,8 +88,7 @@
         mFile.delete();
 
         mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
-                new WindowManagerGlobalLock());
-        mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
+                new WindowManagerGlobalLock(), 1024);
     }
 
     @After
@@ -103,13 +102,13 @@
     }
 
     @Test
-    public void isEnabled_returnsTrueAfterStart() throws Exception {
+    public void isEnabled_returnsTrueAfterStart() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         assertTrue(mWindowTracing.isEnabled());
     }
 
     @Test
-    public void isEnabled_returnsFalseAfterStop() throws Exception {
+    public void isEnabled_returnsFalseAfterStop() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
         assertFalse(mWindowTracing.isEnabled());
@@ -133,6 +132,8 @@
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
 
+        assertTrue("Trace file should exist", mFile.exists());
+
         byte[] header = new byte[MAGIC_HEADER.length];
         try (InputStream is = new FileInputStream(mFile)) {
             assertEquals(MAGIC_HEADER.length, is.read(header));
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 649b785..99ceb20 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -31,6 +31,7 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 
 import org.junit.Before;
@@ -42,6 +43,7 @@
  * Build/Install/Run:
  *  atest WmTests:CoordinateTransformsTest
  */
+@Presubmit
 public class CoordinateTransformsTest {
 
     private static final int W = 200;
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index f1ddfe4..8feed7f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.usage;
 
-import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
@@ -302,27 +301,6 @@
                 UsageStats usageStats = packageStats.valueAt(i);
                 usageStats.update(null, timeStamp, eventType, instanceId);
             }
-        } else if (eventType == ACTIVITY_DESTROYED) {
-            UsageStats usageStats = packageStats.get(packageName);
-            if (usageStats != null) {
-                // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED
-                // to ACTIVITY_STOPPED and add to event list.
-                // Otherwise do not add anything to event list. (Because we want to save space
-                // and we do not want a ACTIVITY_STOPPED followed by
-                // ACTIVITY_DESTROYED in event list).
-                final int index = usageStats.mActivities.indexOfKey(instanceId);
-                if (index >= 0) {
-                    final int type = usageStats.mActivities.valueAt(index);
-                    if (type != ACTIVITY_STOPPED) {
-                        Event event = new Event(ACTIVITY_STOPPED, timeStamp);
-                        event.mPackage = packageName;
-                        event.mClass = className;
-                        event.mInstanceId = instanceId;
-                        addEvent(event);
-                    }
-                }
-                usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId);
-            }
         } else {
             UsageStats usageStats = getOrCreateUsageStats(packageName);
             usageStats.update(className, timeStamp, eventType, instanceId);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index af5278f..ebb0210 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -145,8 +145,16 @@
     AppTimeLimitController mAppTimeLimit;
 
     final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
-    final SparseArray<String> mVisibleActivities = new SparseArray();
+    final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
 
+    private static class ActivityData {
+        private final String mTaskRootPackage;
+        private final String mTaskRootClass;
+        private ActivityData(String taskRootPackage, String taskRootClass) {
+            mTaskRootPackage = taskRootPackage;
+            mTaskRootClass = taskRootClass;
+        }
+    }
 
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@@ -464,47 +472,57 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             convertToSystemTimeLocked(event);
 
-            if (event.getPackageName() != null
-                    && mPackageManagerInternal.isPackageEphemeral(userId, event.getPackageName())) {
+            if (event.mPackage != null
+                    && mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) {
                 event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            service.reportEvent(event);
-
-            mAppStandby.reportEvent(event, elapsedRealtime, userId);
-
-            String packageName;
-
-            switch(mUsageSource) {
-                case USAGE_SOURCE_CURRENT_ACTIVITY:
-                    packageName = event.getPackageName();
-                    break;
-                case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
-                default:
-                    packageName = event.getTaskRootPackageName();
-                    if (packageName == null) {
-                        packageName = event.getPackageName();
-                    }
-                    break;
-            }
-
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    synchronized (mVisibleActivities) {
-                        // check if this activity has already been resumed
-                        if (mVisibleActivities.get(event.mInstanceId) != null) break;
-                        mVisibleActivities.put(event.mInstanceId, event.getClassName());
-                        try {
-                            mAppTimeLimit.noteUsageStart(packageName, userId);
-                        } catch (IllegalArgumentException iae) {
-                            Slog.e(TAG, "Failed to note usage start", iae);
+                    // check if this activity has already been resumed
+                    if (mVisibleActivities.get(event.mInstanceId) != null) break;
+                    mVisibleActivities.put(event.mInstanceId,
+                            new ActivityData(event.mTaskRootPackage, event.mTaskRootClass));
+                    try {
+                        switch(mUsageSource) {
+                            case USAGE_SOURCE_CURRENT_ACTIVITY:
+                                mAppTimeLimit.noteUsageStart(event.mPackage, userId);
+                                break;
+                            case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+                            default:
+                                mAppTimeLimit.noteUsageStart(event.mTaskRootPackage, userId);
+                                break;
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage start", iae);
+                    }
+                    break;
+                case Event.ACTIVITY_PAUSED:
+                    if (event.mTaskRootPackage == null) {
+                        // Task Root info is missing. Repair the event based on previous data
+                        final ActivityData prevData = mVisibleActivities.get(event.mInstanceId);
+                        if (prevData == null) {
+                            Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage
+                                    + "/" + event.mClass + " event : " + event.mEventType
+                                    + " instanceId : " + event.mInstanceId + ")");
+                        } else {
+                            event.mTaskRootPackage = prevData.mTaskRootPackage;
+                            event.mTaskRootClass = prevData.mTaskRootClass;
                         }
                     }
                     break;
-                case Event.ACTIVITY_STOPPED:
                 case Event.ACTIVITY_DESTROYED:
+                    // Treat activity destroys like activity stops.
+                    event.mEventType = Event.ACTIVITY_STOPPED;
+                    // Fallthrough
+                case Event.ACTIVITY_STOPPED:
+                    final ActivityData prevData =
+                            mVisibleActivities.removeReturnOld(event.mInstanceId);
+                    if (prevData == null) {
+                        // The activity stop was already handled.
+                        return;
+                    }
+
                     ArraySet<String> tokens;
                     synchronized (mUsageReporters) {
                         tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
@@ -517,7 +535,7 @@
                                 final String token = tokens.valueAt(i);
                                 try {
                                     mAppTimeLimit.noteUsageStop(
-                                            buildFullToken(event.getPackageName(), token), userId);
+                                            buildFullToken(event.mPackage, token), userId);
                                 } catch (IllegalArgumentException iae) {
                                     Slog.w(TAG, "Failed to stop usage for during reporter death: "
                                             + iae);
@@ -525,18 +543,32 @@
                             }
                         }
                     }
-
-                    synchronized (mVisibleActivities) {
-                        if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
-                            try {
-                                mAppTimeLimit.noteUsageStop(packageName, userId);
-                            } catch (IllegalArgumentException iae) {
-                                Slog.w(TAG, "Failed to note usage stop", iae);
-                            }
+                    if (event.mTaskRootPackage == null) {
+                        // Task Root info is missing. Repair the event based on previous data
+                        event.mTaskRootPackage = prevData.mTaskRootPackage;
+                        event.mTaskRootClass = prevData.mTaskRootClass;
+                    }
+                    try {
+                        switch(mUsageSource) {
+                            case USAGE_SOURCE_CURRENT_ACTIVITY:
+                                mAppTimeLimit.noteUsageStop(event.mPackage, userId);
+                                break;
+                            case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+                            default:
+                                mAppTimeLimit.noteUsageStop(event.mTaskRootPackage, userId);
+                                break;
                         }
+                    } catch (IllegalArgumentException iae) {
+                        Slog.w(TAG, "Failed to note usage stop", iae);
                     }
                     break;
             }
+
+            final UserUsageStatsService service =
+                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            service.reportEvent(event);
+
+            mAppStandby.reportEvent(event, elapsedRealtime, userId);
         }
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 93f758c..b9440eb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1193,7 +1193,7 @@
         }
 
         @Override
-        public void setTranscription(IVoiceInteractionService service, String transcription) {
+        public void setUiHints(IVoiceInteractionService service, Bundle hints) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService(service);
 
@@ -1202,47 +1202,9 @@
                     final IVoiceInteractionSessionListener listener =
                             mVoiceInteractionSessionListeners.getBroadcastItem(i);
                     try {
-                        listener.onTranscriptionUpdate(transcription);
+                        listener.onSetUiHints(hints);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering voice transcription.", e);
-                    }
-                }
-                mVoiceInteractionSessionListeners.finishBroadcast();
-            }
-        }
-
-        @Override
-        public void clearTranscription(IVoiceInteractionService service, boolean immediate) {
-            synchronized (this) {
-                enforceIsCurrentVoiceInteractionService(service);
-
-                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-                for (int i = 0; i < size; ++i) {
-                    final IVoiceInteractionSessionListener listener =
-                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                    try {
-                        listener.onTranscriptionComplete(immediate);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering transcription complete event.", e);
-                    }
-                }
-                mVoiceInteractionSessionListeners.finishBroadcast();
-            }
-        }
-
-        @Override
-        public void setVoiceState(IVoiceInteractionService service, int state) {
-            synchronized (this) {
-                enforceIsCurrentVoiceInteractionService(service);
-
-                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-                for (int i = 0; i < size; ++i) {
-                    final IVoiceInteractionSessionListener listener =
-                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                    try {
-                        listener.onVoiceStateChange(state);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering voice state change.", e);
+                        Slog.e(TAG, "Error delivering UI hints.", e);
                     }
                 }
                 mVoiceInteractionSessionListeners.finishBroadcast();
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bd0d4ae..ae12a17 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -623,7 +623,7 @@
             "android.telecom.event.HANDOVER_FAILED";
 
     /**
-     * Connection extra key used to store SIP invite fields for an incoming call for IMS calls
+     * String Connection extra key used to store SIP invite fields for an incoming call for IMS call
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 0eb9917..16791a4 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.telecom.Logging.EventManager;
 import android.telecom.Logging.Session;
@@ -29,8 +28,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.IllegalFormatException;
 import java.util.Locale;
 
@@ -373,6 +370,12 @@
         return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
     }
 
+    /**
+     * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone
+     * phone number in {@link String} format.
+     * @param pii The information to obfuscate.
+     * @return The obfuscated string.
+     */
     public static String piiHandle(Object pii) {
         if (pii == null || VERBOSE) {
             return String.valueOf(pii);
@@ -389,16 +392,7 @@
 
             String textToObfuscate = uri.getSchemeSpecificPart();
             if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
-                int numDigitsToObfuscate = getDialableCount(textToObfuscate)
-                        - NUM_DIALABLE_DIGITS_TO_LOG;
-                for (int i = 0; i < textToObfuscate.length(); i++) {
-                    char c = textToObfuscate.charAt(i);
-                    boolean isDialable = PhoneNumberUtils.isDialable(c);
-                    if (isDialable) {
-                        numDigitsToObfuscate--;
-                    }
-                    sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c);
-                }
+                obfuscatePhoneNumber(sb, textToObfuscate);
             } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
                 for (int i = 0; i < textToObfuscate.length(); i++) {
                     char c = textToObfuscate.charAt(i);
@@ -410,12 +404,34 @@
             } else {
                 sb.append(pii(pii));
             }
+        } else if (pii instanceof String) {
+            String number = (String) pii;
+            obfuscatePhoneNumber(sb, number);
         }
 
         return sb.toString();
     }
 
     /**
+     * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the
+     * phone number.
+     * @param sb String buffer to write obfuscated number to.
+     * @param phoneNumber The number to obfuscate.
+     */
+    private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) {
+        int numDigitsToObfuscate = getDialableCount(phoneNumber)
+                - NUM_DIALABLE_DIGITS_TO_LOG;
+        for (int i = 0; i < phoneNumber.length(); i++) {
+            char c = phoneNumber.charAt(i);
+            boolean isDialable = PhoneNumberUtils.isDialable(c);
+            if (isDialable) {
+                numDigitsToObfuscate--;
+            }
+            sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c);
+        }
+    }
+
+    /**
      * Determines the number of dialable characters in a string.
      * @param toCount The string to count dialable characters in.
      * @return The count of dialable characters.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e99a289..d509168 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -291,6 +291,19 @@
             "android.telecom.extra.OUTGOING_CALL_EXTRAS";
 
     /**
+     * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell
+     * whether the user's dial intent is emergency; this is required to specify when the dialed
+     * number is ambiguous, identified as both emergency number and any other non-emergency number;
+     * e.g. in some situation, 611 could be both an emergency number in a country and a
+     * non-emergency number of a carrier's customer service hotline.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL =
+            "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+
+    /**
      * @hide
      */
     public static final String EXTRA_UNKNOWN_CALL_HANDLE =
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index a1c32b5..2462bee 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2123,6 +2123,11 @@
      * @hide - not meant for public use
      */
     public interface RcsColumns {
+        // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
+        //  their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
+        //  the latest version of the database before turning this flag to true.
+        boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
+
         /**
          * The authority for the content provider
          */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0c63b60..1b7228a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2424,6 +2424,14 @@
             "5g_icon_configuration_string";
 
     /**
+     * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
+     * this feature.
+     * @hide
+     */
+    public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL =
+            "ascii_7_bit_support_for_long_message_bool";
+
+    /**
      * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
      * the opportunistic network is good enough for internet data.
      */
@@ -2961,6 +2969,7 @@
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+        sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 6b1b84c..856f081 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -30,6 +30,7 @@
     private final int mNrArfcn;
     private final int mPci;
     private final int mTac;
+    private final long mNci;
 
     /**
      *
@@ -44,11 +45,12 @@
      * @hide
      */
     public CellIdentityNr(int pci, int tac, int nrArfcn,  String mccStr, String mncStr,
-            String alphal, String alphas) {
+            long nci, String alphal, String alphas) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = pci;
         mTac = tac;
         mNrArfcn = nrArfcn;
+        mNci = nci;
     }
 
     /**
@@ -62,7 +64,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn);
+        return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci);
     }
 
     @Override
@@ -72,7 +74,17 @@
         }
 
         CellIdentityNr o = (CellIdentityNr) other;
-        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn;
+        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
+                && mNci == o.mNci;
+    }
+
+    /**
+     * Get the NR Cell Identity.
+     *
+     * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown.
+     */
+    public long getNci() {
+        return mNci;
     }
 
     /**
@@ -122,6 +134,7 @@
                 .append(" mNrArfcn = ").append(mNrArfcn)
                 .append(" mMcc = ").append(mMccStr)
                 .append(" mMnc = ").append(mMncStr)
+                .append(" mNci = ").append(mNci)
                 .append(" mAlphaLong = ").append(mAlphaLong)
                 .append(" mAlphaShort = ").append(mAlphaShort)
                 .append(" }")
@@ -134,6 +147,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mNrArfcn);
+        dest.writeLong(mNci);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -142,6 +156,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mNrArfcn = in.readInt();
+        mNci = in.readLong();
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 3814333..dba437a 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -141,6 +141,14 @@
         return mCpid;
     }
 
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
     /** @hide */
     @Override
     public int getChannelNumber() {
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 53d69f4..d98f37d 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -26,11 +26,12 @@
 import android.content.pm.UserInfo;
 import android.location.LocationManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Process;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.util.List;
 
@@ -41,59 +42,234 @@
 public final class LocationAccessPolicy {
     private static final String TAG = "LocationAccessPolicy";
     private static final boolean DBG = false;
+    public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.CUR_DEVELOPMENT;
 
-    /**
-     * 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, boolean throwOnDeniedPermission) throws SecurityException {
-        Trace.beginSection("TelephonyLocationCheck");
-        try {
-            // Always allow the phone process and system server to access location. This avoid
-            // breaking legacy code that rely on public-facing APIs to access cell location, and
-            // it doesn't create an info leak risk because the cell location is stored in the phone
-            // process anyway, and the system server already has location access.
-            if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
-                return true;
-            }
+    public enum LocationPermissionResult {
+        ALLOWED,
+        /**
+         * Indicates that the denial is due to a transient device state
+         * (e.g. app-ops, location master switch)
+         */
+        DENIED_SOFT,
+        /**
+         * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+         */
+        DENIED_HARD,
+    }
 
-            // We always require the location permission and also require the
-            // location mode to be on for non-legacy apps. Legacy apps are
-            // required to be in the foreground to at least mitigate the case
-            // 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 (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) {
-                if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
-                return false;
-            }
-            final int opCode = AppOpsManager.permissionToOpCode(
-                    Manifest.permission.ACCESS_COARSE_LOCATION);
-            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
-                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
-                if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
-                return false;
-            }
-            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
-                if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
-                return false;
-            }
-            // If the user or profile is current, permission is granted.
-            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
-            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
-        } finally {
-            Trace.endSection();
+    public static class LocationPermissionQuery {
+        public final String callingPackage;
+        public final int callingUid;
+        public final int callingPid;
+        public final int minSdkVersionForCoarse;
+        public final int minSdkVersionForFine;
+        public final String method;
+
+        private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid,
+                int minSdkVersionForCoarse, int minSdkVersionForFine, String method) {
+            this.callingPackage = callingPackage;
+            this.callingUid = callingUid;
+            this.callingPid = callingPid;
+            this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+            this.minSdkVersionForFine = minSdkVersionForFine;
+            this.method = method;
         }
+
+        public static class Builder {
+            private String mCallingPackage;
+            private int mCallingUid;
+            private int mCallingPid;
+            private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
+            private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+            private String mMethod;
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPackage(String callingPackage) {
+                mCallingPackage = callingPackage;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingUid(int callingUid) {
+                mCallingUid = callingUid;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPid(int callingPid) {
+                mCallingPid = callingPid;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for coarse location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForCoarse(
+                    int minSdkVersionForCoarse) {
+                mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for fine location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForFine(
+                    int minSdkVersionForFine) {
+                mMinSdkVersionForFine = minSdkVersionForFine;
+                return this;
+            }
+
+            /**
+             * Optional, for logging purposes only.
+             */
+            public Builder setMethod(String method) {
+                mMethod = method;
+                return this;
+            }
+
+            public LocationPermissionQuery build() {
+                return new LocationPermissionQuery(mCallingPackage, mCallingUid,
+                        mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod);
+            }
+        }
+    }
+
+    private static void logError(Context context, String errorMsg) {
+        Log.e(TAG, errorMsg);
+        try {
+            if (Build.IS_DEBUGGABLE) {
+                Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+            }
+        } catch (Throwable t) {
+            // whatever, not important
+        }
+    }
+
+    private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+        switch (appOpsMode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return LocationPermissionResult.ALLOWED;
+            case AppOpsManager.MODE_ERRORED:
+                return LocationPermissionResult.DENIED_HARD;
+            default:
+                return LocationPermissionResult.DENIED_SOFT;
+        }
+    }
+
+    private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+            LocationPermissionQuery query, String permissionToCheck) {
+        String locationTypeForLog =
+                Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                        ? "fine" : "coarse";
+
+        // Do the app-ops and the manifest check without any of the allow-overrides first.
+        boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+                query.callingUid, permissionToCheck);
+
+        int appOpMode = context.getSystemService(AppOpsManager.class)
+                .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
+                        query.callingUid, query.callingPackage);
+
+        if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) {
+            // If the app did everything right, return without logging.
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // If the app has the manifest permission but not the app-op permission, it means that
+        // it's aware of the requirement and the user denied permission explicitly. If we see
+        // this, don't let any of the overrides happen.
+        if (hasManifestPermission) {
+            Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+                    + " app-ops permission is specifically denied.");
+            return appOpsModeToPermissionResult(appOpMode);
+        }
+
+        int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+        // If the app fails for some reason, see if it should be allowed to proceed.
+        if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because we're not enforcing API " + minSdkVersion + " yet."
+                    + " Please fix this app because it will break in the future. Called from "
+                    + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because it doesn't target API " + minSdkVersion + " yet."
+                    + " Please fix this app. Called from " + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else {
+            // If we're not allowing it due to the above two conditions, this means that the app
+            // did not declare the permission in their manifest.
+            return LocationPermissionResult.DENIED_HARD;
+        }
+    }
+
+    public static LocationPermissionResult checkLocationPermission(
+            Context context, LocationPermissionQuery query) {
+        // Always allow the phone process and system server to access location. This avoid
+        // breaking legacy code that rely on public-facing APIs to access cell location, and
+        // it doesn't create an info leak risk because the cell location is stored in the phone
+        // process anyway, and the system server already has location access.
+        if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.ROOT_UID) {
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // Check the system-wide requirements. If the location master switch is off or
+        // the app's profile isn't in foreground, return a soft denial.
+        if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) {
+            return LocationPermissionResult.DENIED_SOFT;
+        }
+
+        // Do the check for fine, then for coarse.
+        if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+            if (resultForFine != null) {
+                return resultForFine;
+            }
+        }
+
+        if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (resultForCoarse != null) {
+                return resultForCoarse;
+            }
+        }
+
+        // At this point, we're out of location checks to do. If the app bypassed all the previous
+        // ones due to the SDK grandfathering schemes, allow it access.
+        return LocationPermissionResult.ALLOWED;
+    }
+
+
+    private static boolean checkManifestPermission(Context context, int pid, int uid,
+            String permissionToCheck) {
+        return context.checkPermission(permissionToCheck, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) {
+        if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+            if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+            return false;
+        }
+        // If the user or profile is current, permission is granted.
+        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
     }
 
     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
@@ -105,10 +281,10 @@
         return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
     }
 
-    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
-        return context.checkCallingOrSelfPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                == PackageManager.PERMISSION_GRANTED;
+    private static boolean checkInteractAcrossUsersFull(
+            @NonNull Context context, int pid, int uid) {
+        return checkManifestPermission(context, pid, uid,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     private static boolean isCurrentProfile(@NonNull Context context, int uid) {
@@ -132,4 +308,18 @@
             Binder.restoreCallingIdentity(token);
         }
     }
-}
+
+    private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+                    >= sdkVersion) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index ceb76b5..6e6d59e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -27,6 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Description of a mobile network registration state
@@ -151,7 +152,7 @@
     private final int[] mAvailableServices;
 
     @Nullable
-    private final CellIdentity mCellIdentity;
+    private CellIdentity mCellIdentity;
 
     @Nullable
     private VoiceSpecificRegistrationStates mVoiceSpecificStates;
@@ -360,7 +361,34 @@
         return 0;
     }
 
-    private static String regStateToString(int regState) {
+    /**
+     * Convert service type to string
+     *
+     * @hide
+     *
+     * @param serviceType The service type
+     * @return The service type in string format
+     */
+    public static String serviceTypeToString(@ServiceType int serviceType) {
+        switch (serviceType) {
+            case SERVICE_TYPE_VOICE: return "VOICE";
+            case SERVICE_TYPE_DATA: return "DATA";
+            case SERVICE_TYPE_SMS: return "SMS";
+            case SERVICE_TYPE_VIDEO: return "VIDEO";
+            case SERVICE_TYPE_EMERGENCY: return "EMERGENCY";
+        }
+        return "Unknown service type " + serviceType;
+    }
+
+    /**
+     * Convert registration state to string
+     *
+     * @hide
+     *
+     * @param regState The registration state
+     * @return The reg state in string
+     */
+    public static String regStateToString(@RegState int regState) {
         switch (regState) {
             case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
             case REG_STATE_HOME: return "HOME";
@@ -389,14 +417,17 @@
     public String toString() {
         return new StringBuilder("NetworkRegistrationState{")
                 .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
-                .append("transportType=").append(mTransportType)
+                .append(" transportType=").append(TransportType.toString(mTransportType))
                 .append(" regState=").append(regStateToString(mRegState))
-                .append(" roamingType=").append(mRoamingType)
+                .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType))
                 .append(" accessNetworkTechnology=")
                 .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
                 .append(" rejectCause=").append(mRejectCause)
                 .append(" emergencyEnabled=").append(mEmergencyOnly)
-                .append(" supportedServices=").append(mAvailableServices)
+                .append(" availableServices=").append("[" + (mAvailableServices != null
+                        ? Arrays.stream(mAvailableServices)
+                        .mapToObj(type -> serviceTypeToString(type))
+                        .collect(Collectors.joining(",")) : null) + "]")
                 .append(" cellIdentity=").append(mCellIdentity)
                 .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
                 .append(" dataSpecificStates=").append(mDataSpecificStates)
@@ -490,4 +521,22 @@
             return new NetworkRegistrationState[size];
         }
     };
+
+    /**
+     * @hide
+     */
+    public NetworkRegistrationState sanitizeLocationInfo() {
+        NetworkRegistrationState result = copy();
+        result.mCellIdentity = null;
+        return result;
+    }
+
+    private NetworkRegistrationState copy() {
+        Parcel p = Parcel.obtain();
+        this.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        NetworkRegistrationState result = new NetworkRegistrationState(p);
+        p.recycle();
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 2c9ba1d..ffebc04 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -46,7 +46,7 @@
  * Override the methods for the state that you wish to receive updates for, and
  * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
  * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
- * called when the state changes, os well as once on initial registration.
+ * called when the state changes, as well as once on initial registration.
  * <p>
  * Note that access to some telephony information is
  * permission-protected. Your application won't receive updates for protected
@@ -297,14 +297,17 @@
     public static final int LISTEN_PHONE_CAPABILITY_CHANGE                 = 0x00200000;
 
     /**
-     *  Listen for changes to preferred data subId.
-     *  See {@link SubscriptionManager#setPreferredDataSubId(int)}
-     *  for more details.
+     *  Listen for changes to active data subId. Active data subscription
+     *  is whichever is being used for Internet data. For most of the case, it's
+     *  default data subscription but it could be others. For example, when data is
+     *  switched to opportunistic subscription, that becomes the active data sub.
      *
-     *  @see #onPreferredDataSubIdChanged
+     *  Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+     *  READ_PHONE_STATE}
+     *  @see #onActiveDataSubIdChanged
      *  @hide
      */
-    public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE              = 0x00400000;
+    public static final int LISTEN_ACTIVE_DATA_SUBID_CHANGE               = 0x00400000;
 
     /**
      *  Listen for changes to the radio power state.
@@ -710,14 +713,14 @@
     }
 
     /**
-     * Callback invoked when preferred data subId changes. Requires
-     * the READ_PRIVILEGED_PHONE_STATE permission.
-     * @param subId the new preferred data subId. If it's INVALID_SUBSCRIPTION_ID,
-     *              it means it's unset and defaultDataSub is used to determine which
-     *              modem is preferred.
+     * Callback invoked when active data subId changes. Requires
+     * the READ_PHONE_STATE permission.
+     * @param subId current data subId used for Internet data. It will be default data subscription
+     *              most cases. And it could be other subscriptions for example opportunistic
+     *              subscription if data is switched onto it.
      * @hide
      */
-    public void onPreferredDataSubIdChanged(int subId) {
+    public void onActiveDataSubIdChanged(int subId) {
         // default implementation empty
     }
 
@@ -1001,12 +1004,12 @@
                     () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
         }
 
-        public void onPreferredDataSubIdChanged(int subId) {
+        public void onActiveDataSubIdChanged(int subId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
 
             Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> psl.onPreferredDataSubIdChanged(subId)));
+                    () -> mExecutor.execute(() -> psl.onActiveDataSubIdChanged(subId)));
         }
 
         public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 3317876..a1aee6d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,6 +36,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Contains phone state and service related information.
@@ -887,6 +888,24 @@
     }
 
     /**
+     * Convert roaming type to string
+     *
+     * @param roamingType roaming type
+     * @return The roaming type in string format
+     *
+     * @hide
+     */
+    public static String roamingTypeToString(@RoamingType int roamingType) {
+        switch (roamingType) {
+            case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING";
+            case ROAMING_TYPE_UNKNOWN: return "UNKNOWN";
+            case ROAMING_TYPE_DOMESTIC: return "DOMESTIC";
+            case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL";
+        }
+        return "Unknown roaming type " + roamingType;
+    }
+
+    /**
      * Convert radio technology to String
      *
      * @param rt radioTechnology
@@ -1867,4 +1886,29 @@
                 ? range1
                 : range2;
     }
+
+    /**
+     * Returns a copy of self with location-identifying information removed.
+     * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation
+     * is true, clears other info as well.
+     * @hide
+     */
+    public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+        ServiceState state = new ServiceState(this);
+        if (state.mNetworkRegistrationStates != null) {
+            state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream()
+                    .map(NetworkRegistrationState::sanitizeLocationInfo)
+                    .collect(Collectors.toList());
+        }
+        if (!removeCoarseLocation) return state;
+
+        state.mDataOperatorAlphaLong = null;
+        state.mDataOperatorAlphaShort = null;
+        state.mDataOperatorNumeric = null;
+        state.mVoiceOperatorAlphaLong = null;
+        state.mVoiceOperatorAlphaShort = null;
+        state.mVoiceOperatorNumeric = null;
+
+        return state;
+    }
 }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 099015f..2aa4768 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -981,4 +981,13 @@
 
         return false;
     }
+
+    /**
+     * {@hide}
+     * Returns the recipient address(receiver) of this SMS message in String form or null if
+     * unavailable.
+     */
+    public String getRecipientAddress() {
+        return mWrappedSmsMessage.getRecipientAddress();
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3a4d33c..94f26a8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -866,7 +866,8 @@
         }
 
         /**
-         * Callback invoked when there is any change to any SubscriptionInfo. Typically
+         * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+         * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
          * this method would invoke {@link #getActiveSubscriptionInfoList}
          */
         public void onSubscriptionsChanged() {
@@ -918,7 +919,9 @@
     /**
      * Register for changes to the list of active {@link SubscriptionInfo} records or to the
      * individual records themselves. When a change occurs the onSubscriptionsChanged method of
-     * the listener will be invoked immediately if there has been a notification.
+     * the listener will be invoked immediately if there has been a notification. The
+     * onSubscriptionChanged method will also be triggered once initially when calling this
+     * function.
      *
      * @param listener an instance of {@link OnSubscriptionsChangedListener} with
      *                 onSubscriptionsChanged overridden.
@@ -1233,7 +1236,7 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        return getActiveSubscriptionInfoList(false);
+        return getActiveSubscriptionInfoList(/* userVisibleonly */true);
     }
 
     /**
@@ -1861,7 +1864,7 @@
                 iSub.setDefaultSmsSubId(subscriptionId);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            ex.rethrowFromSystemServer();
         }
     }
 
@@ -2855,15 +2858,24 @@
     /**
      * Whether system UI should hide a subscription. If it's a bundled opportunistic
      * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
-     * or status bar.
+     * or status bar. Exception is if caller is carrier app, in which case they will
+     * want to see their own hidden subscriptions.
      *
      * @param info the subscriptionInfo to check against.
      * @return true if this subscription should be hidden.
      *
      * @hide
      */
-    public static boolean shouldHideSubscription(SubscriptionInfo info) {
-        return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+    private boolean shouldHideSubscription(SubscriptionInfo info) {
+        if (info == null) return false;
+
+        // If hasCarrierPrivileges or canManageSubscription returns true, it means caller
+        // has carrier privilege.
+        boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info))
+                || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId());
+
+        return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()
+                && !hasCarrierPrivilegePermission);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6690bd0..ced4f4a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -229,10 +229,19 @@
     public static final int SRVCC_STATE_HANDOVER_CANCELED  = 3;
 
     /**
-     * An invalid UICC card identifier. See {@link #getCardIdForDefaultEuicc()} and
-     * {@link UiccCardInfo#getCardId()}.
+     * A UICC card identifier used if the device does not support the operation.
+     * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no
+     * eUICC, or the eUICC cannot be read.
      */
-    public static final int INVALID_CARD_ID = -1;
+    public static final int UNSUPPORTED_CARD_ID = -1;
+
+    /**
+     * A UICC card identifier used before the UICC card is loaded. See
+     * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}.
+     * <p>
+     * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}.
+     */
+    public static final int UNINITIALIZED_CARD_ID = -2;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -1689,10 +1698,7 @@
      * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_COARSE_LOCATION,
-            android.Manifest.permission.ACCESS_FINE_LOCATION
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
@@ -3178,24 +3184,25 @@
     }
 
     /**
-     * Get the card ID of the default eUICC card. If there is no eUICC, returns
-     * {@link #INVALID_CARD_ID}.
+     * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns
+     * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs
+     * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}.
      *
      * <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are
      * unique to a device, and always refer to the same UICC or eUICC card unless the device goes
      * through a factory reset.
      *
-     * @return card ID of the default eUICC card.
+     * @return card ID of the default eUICC card, if loaded.
      */
     public int getCardIdForDefaultEuicc() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null) {
-                return INVALID_CARD_ID;
+                return UNINITIALIZED_CARD_ID;
             }
             return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName());
         } catch (RemoteException e) {
-            return INVALID_CARD_ID;
+            return UNINITIALIZED_CARD_ID;
         }
     }
 
@@ -4941,7 +4948,7 @@
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public List<CellInfo> getAllCellInfo() {
         try {
             ITelephony telephony = getITelephony();
@@ -5018,7 +5025,7 @@
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void requestCellInfoUpdate(
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
@@ -5057,7 +5064,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
             android.Manifest.permission.MODIFY_PHONE_STATE})
     public void requestCellInfoUpdate(@NonNull WorkSource workSource,
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
@@ -6647,9 +6654,10 @@
      *
      * <p> Note that this scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>Requires Permission:
+     * <p>Requires Permissions:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
      * privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      *
      * @return {@link CellNetworkScanResult} with the status
      * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
@@ -6658,12 +6666,15 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public CellNetworkScanResult getAvailableNetworks() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getCellNetworkScanResults(getSubId());
+                return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName());
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -6682,7 +6693,8 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
      * @param executor The executor through which the callback should be invoked. Since the scan
@@ -6693,7 +6705,10 @@
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
             TelephonyScanManager.NetworkScanCallback callback) {
@@ -6702,7 +6717,8 @@
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+                getOpPackageName());
     }
 
     /**
@@ -6712,7 +6728,10 @@
      * @removed
      */
     @Deprecated
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
         return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -8713,10 +8732,14 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96ff332..91f74b8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -29,14 +29,14 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.Log;
 import android.util.SparseArray;
+
+import com.android.internal.telephony.ITelephony;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-import com.android.internal.telephony.ITelephony;
-
 /**
  * Manages the radio access network scan requests and callbacks.
  */
@@ -183,6 +183,7 @@
      *
      * <p>
      * Requires Permission:
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
@@ -192,11 +193,13 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
-            NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+            String callingPackage) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+                int scanId = telephony.requestNetworkScan(
+                        subId, request, mMessenger, new Binder(), callingPackage);
                 saveScanInfo(scanId, request, executor, callback);
                 return new NetworkScan(scanId, subId);
             }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 1bbf9f4..ad34349 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -460,7 +460,7 @@
      */
     @Nullable
     public String getEid() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return null;
         }
         try {
@@ -483,7 +483,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
         }
         try {
@@ -518,7 +518,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -580,7 +580,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             PendingIntent callbackIntent =
                     resolutionIntent.getParcelableExtra(
                             EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -617,7 +617,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -647,7 +647,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -666,7 +666,7 @@
      */
     @Nullable
     public EuiccInfo getEuiccInfo() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return null;
         }
         try {
@@ -691,7 +691,7 @@
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -731,7 +731,7 @@
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -757,7 +757,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -781,7 +781,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -811,7 +811,7 @@
      * @hide
      */
     public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -822,16 +822,24 @@
         }
     }
 
-    private boolean refreshCardIdIfInvalid() {
-        if (!isEnabled()) {
-            return false;
-        }
-        // Refresh mCardId if it's invalid.
-        if (mCardId == TelephonyManager.INVALID_CARD_ID) {
+    /**
+     * Refreshes the cardId if its uninitialized, and returns whether we should continue the
+     * operation.
+     * <p>
+     * Note that after a successful refresh, the mCardId may be TelephonyManager.UNSUPPORTED_CARD_ID
+     * on older HALs. For backwards compatability, we continue to the LPA and let it decide which
+     * card to use.
+     */
+    private boolean refreshCardIdIfUninitialized() {
+        // Refresh mCardId if its UNINITIALIZED_CARD_ID
+        if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
             TelephonyManager tm = (TelephonyManager)
                     mContext.getSystemService(Context.TELEPHONY_SERVICE);
             mCardId = tm.getCardIdForDefaultEuicc();
         }
+        if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
+            return false;
+        }
         return true;
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 59167b7..47c8de5 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -266,6 +266,11 @@
     public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
     public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * String extra property
+     *  Containing fields from the SIP INVITE message for an IMS call
+     */
     public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
                                   "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
@@ -350,6 +355,9 @@
     /** Indicates if the call is for testing purpose */
     private boolean mEmergencyCallTesting = false;
 
+    /** Indicates if we have known the intent of the user for the call is emergency */
+    private boolean mHasKnownUserIntentEmergency = false;
+
     /**
      * Extras associated with this {@link ImsCallProfile}.
      * <p>
@@ -540,7 +548,8 @@
                 + ", emergencyServiceCategories=" + mEmergencyServiceCategories
                 + ", emergencyUrns=" + mEmergencyUrns
                 + ", emergencyCallRouting=" + mEmergencyCallRouting
-                + ", emergencyCallTesting=" + mEmergencyCallTesting + " }";
+                + ", emergencyCallTesting=" + mEmergencyCallTesting
+                + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency + " }";
     }
 
     @Override
@@ -559,6 +568,7 @@
         out.writeStringList(mEmergencyUrns);
         out.writeInt(mEmergencyCallRouting);
         out.writeBoolean(mEmergencyCallTesting);
+        out.writeBoolean(mHasKnownUserIntentEmergency);
     }
 
     private void readFromParcel(Parcel in) {
@@ -570,6 +580,7 @@
         mEmergencyUrns = in.createStringArrayList();
         mEmergencyCallRouting = in.readInt();
         mEmergencyCallTesting = in.readBoolean();
+        mHasKnownUserIntentEmergency = in.readBoolean();
     }
 
     public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -789,12 +800,13 @@
      *
      * @hide
      */
-    public void setEmergencyCallInfo(EmergencyNumber num) {
+    public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
         setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
         setEmergencyUrns(num.getEmergencyUrns());
         setEmergencyCallRouting(num.getEmergencyCallRouting());
         setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
                 == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+        setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
     }
 
     /**
@@ -860,6 +872,19 @@
     }
 
     /**
+     * Set if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    @VisibleForTesting
+    public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+        mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+    }
+
+    /**
      * Get the emergency service categories, only valid if {@link #getServiceType} returns
      * {@link #SERVICE_TYPE_EMERGENCY}
      *
@@ -916,4 +941,16 @@
     public boolean isEmergencyCallTesting() {
         return mEmergencyCallTesting;
     }
+
+    /**
+     * Checks if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    public boolean hasKnownUserIntentEmergency() {
+        return mHasKnownUserIntentEmergency;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 322ce45..4a263f0 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -54,7 +54,7 @@
     void onCarrierNetworkChange(in boolean active);
     void onUserMobileDataStateChanged(in boolean enabled);
     void onPhoneCapabilityChanged(in PhoneCapability capability);
-    void onPreferredDataSubIdChanged(in int subId);
+    void onActiveDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
     void onCallAttributesChanged(in CallAttributes callAttributes);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index caa367f..7089ee5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -765,7 +765,7 @@
      * @param subId the id of the subscription.
      * @return CellNetworkScanResult containing status of scan and networks.
      */
-    CellNetworkScanResult getCellNetworkScanResults(int subId);
+    CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage);
 
     /**
      * Perform a radio network scan and return the id of this scan.
@@ -774,10 +774,11 @@
      * @param request Defines all the configs for network scan.
      * @param messenger Callback messages will be sent using this messenger.
      * @param binder the binder object instantiated in TelephonyManager.
+     * @param callingPackage the calling package
      * @return An id for this scan.
      */
     int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
-            in IBinder binder);
+            in IBinder binder, in String callingPackage);
 
     /**
      * Stop an existing radio network scan.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index e9eba32..a922ab6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -81,7 +81,7 @@
     void notifyCarrierNetworkChange(in boolean active);
     void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
-    void notifyPreferredDataSubIdChanged(int preferredSubId);
+    void notifyActiveDataSubIdChanged(int activeDataSubId);
     void notifyRadioPowerStateChanged(in int state);
     void notifyEmergencyNumberList();
     void notifyCallQualityChanged(in CallQuality callQuality, int phoneId);
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 190eac4..ffdc4b6 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -41,6 +41,9 @@
     @UnsupportedAppUsage
     protected SmsAddress mOriginatingAddress;
 
+    /** {@hide} The address of the receiver */
+    protected SmsAddress mRecipientAddress;
+
     /** {@hide} The message body as a string. May be null if the message isn't text */
     @UnsupportedAppUsage
     protected String mMessageBody;
@@ -457,4 +460,17 @@
 
         return ted;
     }
+
+    /**
+     * {@hide}
+     * Returns the receiver address of this SMS message in String
+     * form or null if unavailable
+     */
+    public String getRecipientAddress() {
+        if (mRecipientAddress == null) {
+            return null;
+        }
+
+        return mRecipientAddress.getAddressString();
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
index a4cd56b..694cc69 100644
--- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
@@ -596,6 +596,45 @@
         System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
     }
 
+    private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)
+            throws CodingException
+    {
+        try {
+            Rlog.d(LOG_TAG, "encode7bitAsciiEms");
+            int udhBytes = udhData.length + 1;  // Add length octet.
+            int udhSeptets = ((udhBytes * 8) + 6) / 7;
+            int paddingBits = (udhSeptets * 7) - (udhBytes * 8);
+            String msg = uData.payloadStr;
+            byte[] payload ;
+            int msgLen = msg.length();
+            BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen +
+                    (paddingBits > 0 ? 1 : 0));
+            outStream.write(paddingBits, 0);
+            for (int i = 0; i < msgLen; i++) {
+                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
+                if (charCode == -1) {
+                    if (force) {
+                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
+                    } else {
+                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
+                    }
+                } else {
+                    outStream.write(7, charCode);
+                }
+            }
+            payload = outStream.toByteArray();
+            uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+            uData.msgEncodingSet = true;
+            uData.numFields = udhSeptets + uData.payloadStr.length();
+            uData.payload = new byte[udhBytes + payload.length];
+            uData.payload[0] = (byte)udhData.length;
+            System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
+            System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
+        } catch (BitwiseOutputStream.AccessException ex) {
+            throw new CodingException("7bit ASCII encode failed: " + ex);
+        }
+    }
+
     private static void encodeEmsUserDataPayload(UserData uData)
         throws CodingException
     {
@@ -605,6 +644,8 @@
                 encode7bitEms(uData, headerData, true);
             } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
                 encode16bitEms(uData, headerData);
+            } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
+                encode7bitAsciiEms(uData, headerData, true);
             } else {
                 throw new CodingException("unsupported EMS user data encoding (" +
                                           uData.msgEncoding + ")");
@@ -1056,15 +1097,18 @@
         throws CodingException
     {
         try {
-            offset *= 8;
+            int offsetBits = offset * 8;
+            int offsetSeptets = (offsetBits + 6) / 7;
+            numFields -= offsetSeptets;
+
             StringBuffer strBuf = new StringBuffer(numFields);
             BitwiseInputStream inStream = new BitwiseInputStream(data);
-            int wantedBits = (offset * 8) + (numFields * 7);
+            int wantedBits = (offsetSeptets * 7) + (numFields * 7);
             if (inStream.available() < wantedBits) {
                 throw new CodingException("insufficient data (wanted " + wantedBits +
                                           " bits, but only have " + inStream.available() + ")");
             }
-            inStream.skip(offset);
+            inStream.skip(offsetSeptets * 7);
             for (int i = 0; i < numFields; i++) {
                 int charCode = inStream.read(7);
                 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 9080e23..a31fa0b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -601,18 +601,24 @@
 
                             } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
                                 if (numberType == 2)
-                                    Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
+                                    Rlog.e(LOG_TAG, "TODO: Addr is email id");
                                 else
                                     Rlog.e(LOG_TAG,
-                                          "TODO: Originating Addr is data network address");
+                                          "TODO: Addr is data network address");
                             } else {
-                                Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
+                                Rlog.e(LOG_TAG, "Addr is of incorrect type");
                             }
                         } else {
                             Rlog.e(LOG_TAG, "Incorrect Digit mode");
                         }
                         addr.origBytes = data;
-                        Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
+                        Rlog.pii(LOG_TAG, "Addr=" + addr.toString());
+                        mOriginatingAddress = addr;
+                        if (parameterId == DESTINATION_ADDRESS) {
+                            // Original address awlays indicates one sender's address for 3GPP2
+                            // Here add recipient address support along with 3GPP
+                            mRecipientAddress = addr;
+                        }
                         break;
                     case ORIGINATING_SUB_ADDRESS:
                     case DESTINATION_SUB_ADDRESS:
@@ -667,7 +673,7 @@
     }
 
     /**
-     * Parses a SMS message from its BearerData stream. (mobile-terminated only)
+     * Parses a SMS message from its BearerData stream.
      */
     public void parseSms() {
         // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
@@ -697,16 +703,15 @@
         }
 
         if (mOriginatingAddress != null) {
-            mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
-            if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
-                if (mOriginatingAddress.address.charAt(0) != '+') {
-                    mOriginatingAddress.address = "+" + mOriginatingAddress.address;
-                }
-            }
+            decodeSmsDisplayAddress(mOriginatingAddress);
             if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
                     + mOriginatingAddress.address);
         }
 
+        if (mRecipientAddress != null) {
+            decodeSmsDisplayAddress(mRecipientAddress);
+        }
+
         if (mBearerData.msgCenterTimeStamp != null) {
             mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
         }
@@ -731,7 +736,8 @@
                 status = mBearerData.errorClass << 8;
                 status |= mBearerData.messageStatus;
             }
-        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
+        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER
+                && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) {
             throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
         }
 
@@ -743,6 +749,16 @@
         }
     }
 
+    private void decodeSmsDisplayAddress(SmsAddress addr) {
+        addr.address = new String(addr.origBytes);
+        if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
+            if (addr.address.charAt(0) != '+') {
+                addr.address = "+" + addr.address;
+            }
+        }
+        Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address);
+    }
+
     /**
      * Parses a broadcast SMS, possibly containing a CMAS alert.
      *
@@ -864,8 +880,9 @@
             Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
         }
 
-        int teleservice = bearerData.hasUserDataHeader ?
-                SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+        int teleservice = (bearerData.hasUserDataHeader
+                && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII)
+                ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
 
         SmsEnvelope envelope = new SmsEnvelope();
         envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 015efa6..19465a4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -71,9 +71,6 @@
     // e.g. 23.040 9.2.2.1
     private boolean mReplyPathPresent = false;
 
-    /** The address of the receiver. */
-    private GsmSmsAddress mRecipientAddress;
-
     /**
      *  TP-Status - status of a previously submitted SMS.
      *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index ab10800..0cb8f22 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -9,10 +9,12 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public String[] getNamesForUids(int[]);
     method public String getPermissionControllerPackageName();
+    method public int getPermissionFlags(String, String, android.os.UserHandle);
     method @NonNull public String getServicesSystemSharedLibraryPackageName();
     method @NonNull public String getSharedSystemSharedLibraryPackageName();
     method public void grantRuntimePermission(String, String, android.os.UserHandle);
     method public void revokeRuntimePermission(String, String, android.os.UserHandle);
+    method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
   }
 
 }
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index d5549cc..07b3a97 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -439,6 +439,14 @@
                 return true;
             }
         });
+        menu.add("Require unknown permission").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override public boolean onMenuItemClick(MenuItem item) {
+                final Intent intent = new Intent(SLOW_RECEIVER_ACTION);
+                intent.putExtra(SLOW_RECEIVER_EXTRA, 5038);
+                sendOrderedBroadcast(intent, "com.google.android.test.activity.permission.UNDEFINED");
+                return true;
+            }
+        });
         menu.add("Stack Doc").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override public boolean onMenuItemClick(MenuItem item) {
                 ActivityManager.AppTask task = findDocTask();
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
index e92cc56..99d1f5ff4 100644
--- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
@@ -49,13 +49,13 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Integration tests for {@link com.android.server.pm.dex.DexLogger}.
+ * Integration tests for {@link DynamicCodeLogger}, formerly known as {@code DexLogger}.
  *
  * The setup for the test dynamically loads code in a jar extracted
  * from our assets (a secondary dex file).
  *
  * We then use shell commands to trigger dynamic code logging (and wait
- * for it to complete). This causes DexLogger to log the hash of the
+ * for it to complete). This causes DynamicCodeLogger to log the hash of the
  * file's name and content.  We verify that this message appears in
  * the event log.
  *
@@ -65,6 +65,8 @@
 @RunWith(JUnit4.class)
 public final class DexLoggerIntegrationTests {
 
+    private static final String SHA_256 = "SHA-256";
+
     // Event log tag used for SNET related events
     private static final int SNET_TAG = 0x534e4554;
 
@@ -76,6 +78,13 @@
     private static final int IDLE_LOGGING_JOB_ID = 2030028;
     private static final int AUDIT_WATCHING_JOB_ID = 203142925;
 
+    // For tests that rely on parsing audit logs, how often to retry. (There are many reasons why
+    // we might not see the audit logs, including throttling and delays in log generation, so to
+    // avoid flakiness we run these tests multiple times, allowing progressively longer between
+    // code loading and checking the logs on each try.)
+    private static final int AUDIT_LOG_RETRIES = 10;
+    private static final int RETRY_DELAY_MS = 2_000;
+
     private static Context sContext;
     private static int sMyUid;
 
@@ -99,7 +108,7 @@
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_standardClassLoader() throws Exception {
+    public void testGeneratesEvents_standardClassLoader() throws Exception {
         File privateCopyFile = privateFile("copied.jar");
         // Obtained via "echo -n copied.jar | sha256sum"
         String expectedNameHash =
@@ -121,7 +130,7 @@
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception {
+    public void testGeneratesEvents_unknownClassLoader() throws Exception {
         File privateCopyFile = privateFile("copied2.jar");
         String expectedNameHash =
                 "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
@@ -143,79 +152,66 @@
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_nativeLibrary() throws Exception {
-        File privateCopyFile = privateFile("copied.so");
-        String expectedNameHash =
-                "996223BAD4B4FE75C57A3DEC61DB9C0B38E0A7AD479FC95F33494F4BC55A0F0E";
-        String expectedContentHash =
-                copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile);
+    public void testGeneratesEvents_nativeLibrary() throws Exception {
+        new TestNativeCodeWithRetries() {
+            @Override
+            protected void loadNativeCode(int tryNumber) throws Exception {
+                // We need to use a different file name for each retry, because once a file is
+                // loaded, re-loading it has no effect.
+                String privateCopyName = "copied" + tryNumber + ".so";
+                File privateCopyFile = privateFile(privateCopyName);
+                mExpectedNameHash = hashOf(privateCopyName);
+                mExpectedContentHash = copyAndHashResource(
+                        libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile);
 
-        System.load(privateCopyFile.toString());
-
-        // Run the job to scan generated audit log entries
-        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
-
-        // And then make sure we log events about it
-        long previousEventNanos = mostRecentEventTimeNanos();
-        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
-
-        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
-                expectedNameHash, expectedContentHash);
+                System.load(privateCopyFile.toString());
+            }
+        }.runTest();
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_nativeLibrary_escapedName() throws Exception {
-        // A file name with a space will be escaped in the audit log; verify we un-escape it
-        // correctly.
-        File privateCopyFile = privateFile("second copy.so");
-        String expectedNameHash =
-                "8C39990C560B4F36F83E208E279F678746FE23A790E4C50F92686584EA2041CA";
-        String expectedContentHash =
-                copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile);
+    public void testGeneratesEvents_nativeLibrary_escapedName() throws Exception {
+        new TestNativeCodeWithRetries() {
+            @Override
+            protected void loadNativeCode(int tryNumber) throws Exception {
+                // A file name with a space will be escaped in the audit log; verify we un-escape it
+                // correctly.
+                String privateCopyName = "second copy " + tryNumber + ".so";
+                File privateCopyFile = privateFile(privateCopyName);
+                mExpectedNameHash = hashOf(privateCopyName);
+                mExpectedContentHash = copyAndHashResource(
+                        libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile);
 
-        System.load(privateCopyFile.toString());
-
-        // Run the job to scan generated audit log entries
-        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
-
-        // And then make sure we log events about it
-        long previousEventNanos = mostRecentEventTimeNanos();
-        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
-
-        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
-                expectedNameHash, expectedContentHash);
+                System.load(privateCopyFile.toString());
+            }
+        }.runTest();
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_nativeExecutable() throws Exception {
-        File privateCopyFile = privateFile("test_executable");
-        String expectedNameHash =
-                "3FBEC3F925A132D18F347F11AE9A5BB8DE1238828F8B4E064AA86EB68BD46DCF";
-        String expectedContentHash =
-                copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
-        assertThat(privateCopyFile.setExecutable(true)).isTrue();
+    public void testGeneratesEvents_nativeExecutable() throws Exception {
+        new TestNativeCodeWithRetries() {
+            @Override
+            protected void loadNativeCode(int tryNumber) throws Exception {
+                String privateCopyName = "test_executable" + tryNumber;
+                File privateCopyFile = privateFile(privateCopyName);
+                mExpectedNameHash = hashOf(privateCopyName);
+                mExpectedContentHash = copyAndHashResource(
+                        "/DexLoggerNativeExecutable", privateCopyFile);
+                assertThat(privateCopyFile.setExecutable(true)).isTrue();
 
-        Process process = Runtime.getRuntime().exec(privateCopyFile.toString());
-        int exitCode = process.waitFor();
-        assertThat(exitCode).isEqualTo(0);
-
-        // Run the job to scan generated audit log entries
-        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
-
-        // And then make sure we log events about it
-        long previousEventNanos = mostRecentEventTimeNanos();
-        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
-
-        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
-                expectedNameHash, expectedContentHash);
+                Process process = Runtime.getRuntime().exec(privateCopyFile.toString());
+                int exitCode = process.waitFor();
+                assertThat(exitCode).isEqualTo(0);
+            }
+        }.runTest();
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_spoofed_validFile() throws Exception {
+    public void testGeneratesEvents_spoofed_validFile() throws Exception {
         File privateCopyFile = privateFile("spoofed");
 
-        String expectedContentHash =
-                copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
+        String expectedContentHash = copyAndHashResource(
+                "/DexLoggerNativeExecutable", privateCopyFile);
 
         EventLog.writeEvent(EventLog.getTagCode("auditd"),
                 "type=1400 avc: granted { execute_no_trans } "
@@ -239,7 +235,7 @@
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_spoofed_pathTraversal() throws Exception {
+    public void testGeneratesEvents_spoofed_pathTraversal() throws Exception {
         File privateDir = privateFile("x").getParentFile();
 
         // Transform /a/b/c -> /a/b/c/../../.. so we get back to the root
@@ -276,7 +272,7 @@
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents_spoofed_otherAppFile() throws Exception {
+    public void testGeneratesEvents_spoofed_otherAppFile() throws Exception {
         File ourPath = sContext.getDatabasePath("android_pay");
         File targetPath = new File(ourPath.toString()
                 .replace("com.android.frameworks.dexloggertest", "com.google.android.gms"));
@@ -304,6 +300,40 @@
         assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash);
     }
 
+    // Abstract out the logic for running a native code loading test multiple times if needed and
+    // leaving time for audit messages to reach the log.
+    private abstract class TestNativeCodeWithRetries {
+        String mExpectedContentHash;
+        String mExpectedNameHash;
+
+        abstract void loadNativeCode(int tryNumber) throws Exception;
+
+        final void runTest() throws Exception {
+            List<String> messages = null;
+
+            for (int i = 0; i < AUDIT_LOG_RETRIES; i++) {
+                loadNativeCode(i);
+
+                SystemClock.sleep(i * RETRY_DELAY_MS);
+
+                // Run the job to scan generated audit log entries
+                runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+                // And then make sure we log events about it
+                long previousEventNanos = mostRecentEventTimeNanos();
+                runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+                messages = findMatchingEvents(
+                        previousEventNanos, DCL_NATIVE_SUBTAG, mExpectedNameHash);
+                if (!messages.isEmpty()) {
+                    break;
+                }
+            }
+
+            assertHasDclLog(messages, mExpectedContentHash);
+        }
+    }
+
     private static File privateFile(String name) {
         return new File(sContext.getDir("dcl", Context.MODE_PRIVATE), name);
     }
@@ -315,7 +345,7 @@
     }
 
     private static String copyAndHashResource(String resourcePath, File copyTo) throws Exception {
-        MessageDigest hasher = MessageDigest.getInstance("SHA-256");
+        MessageDigest hasher = MessageDigest.getInstance(SHA_256);
 
         // Copy the jar from our Java resources to a private data directory
         Class<?> thisClass = DexLoggerIntegrationTests.class;
@@ -334,6 +364,16 @@
 
         // Compute the SHA-256 of the file content so we can check that it is the same as the value
         // we see logged.
+        return toHexString(hasher);
+    }
+
+    private String hashOf(String input) throws Exception {
+        MessageDigest hasher = MessageDigest.getInstance(SHA_256);
+        hasher.update(input.getBytes());
+        return toHexString(hasher);
+    }
+
+    private static String toHexString(MessageDigest hasher) {
         Formatter formatter = new Formatter();
         for (byte b : hasher.digest()) {
             formatter.format("%02X", b);
@@ -388,6 +428,10 @@
         List<String> messages =
                 findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash);
 
+        assertHasDclLog(messages, expectedContentHash);
+    }
+
+    private static void assertHasDclLog(List<String> messages, String expectedContentHash) {
         assertWithMessage("Expected exactly one matching log entry").that(messages).hasSize(1);
         assertThat(messages.get(0)).endsWith(expectedContentHash);
     }
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
new file mode 100644
index 0000000..37d3f5d
--- /dev/null
+++ b/tests/HwAccelerationTest/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2010 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.
+//
+
+android_test {
+    name: "HwAccelerationTest",
+    srcs: ["**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tests/HwAccelerationTest/Android.mk b/tests/HwAccelerationTest/Android.mk
deleted file mode 100644
index 79072fa..0000000
--- a/tests/HwAccelerationTest/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := HwAccelerationTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a4ec94..7b8c154 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1028,8 +1028,28 @@
         </activity>
 
         <activity
-            android:name="PositionListenerActivity"
-            android:label="RenderNode/PositionListener"
+                android:name="PositionListenerActivity"
+                android:label="RenderNode/PositionListener"
+                android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="CustomRenderer"
+            android:label="HardwareRenderer/HelloTakeSurface"
+            android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="MyLittleTextureView"
+            android:label="HardwareRenderer/MyLittleTextureView"
             android:screenOrientation="fullSensor">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
new file mode 100644
index 0000000..60bd60f
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.HardwareRenderer;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+public class CustomRenderer extends Activity {
+    private RenderNode mContent = new RenderNode("CustomRenderer");
+    private HardwareRenderer mRenderer = new HardwareRenderer();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().takeSurface(mSurfaceCallbacks);
+    }
+
+    private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() {
+
+        @Override
+        public void surfaceRedrawNeeded(SurfaceHolder holder) {
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            mContent.setLeftTopRightBottom(0, 0, width, height);
+            RecordingCanvas canvas = mContent.startRecording();
+            canvas.drawColor(Color.WHITE);
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            paint.setColor(Color.BLACK);
+            paint.setTextAlign(Paint.Align.CENTER);
+            paint.setTextSize(Math.min(width, height) * .05f);
+            canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint);
+            mContent.endRecording();
+
+            mRenderer.setContentRoot(mContent);
+            mRenderer.setSurface(holder.getSurface());
+            mRenderer.createRenderRequest()
+                    .setVsyncTime(System.nanoTime())
+                    .setFrameCommitCallback(Runnable::run, () -> {
+                        Log.d("CustomRenderer", "Frame committed!");
+                    })
+                    .syncAndDraw();
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mRenderer.destroy();
+        }
+    };
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
new file mode 100644
index 0000000..8bd7d79
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.HardwareRenderer;
+import android.graphics.Outline;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class MyLittleTextureView extends Activity {
+    private RenderNode mContent = new RenderNode("CustomRenderer");
+    private HardwareRenderer mRenderer = new HardwareRenderer();
+    private ImageView mImageView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mImageView = new ImageView(this);
+        mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        setContentView(mImageView);
+
+        ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3,
+                HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
+        mRenderer.setSurface(reader.getSurface());
+        mRenderer.setLightSourceAlpha(0.0f, 1.0f);
+        mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f);
+        mContent.setLeftTopRightBottom(0, 0, 100, 100);
+
+        Rect childRect = new Rect(25, 25, 65, 65);
+        RenderNode childNode = new RenderNode("shadowCaster");
+        childNode.setLeftTopRightBottom(childRect.left, childRect.top,
+                childRect.right, childRect.bottom);
+        Outline outline = new Outline();
+        outline.setRect(new Rect(0, 0, childRect.width(), childRect.height()));
+        outline.setAlpha(1f);
+        childNode.setOutline(outline);
+        {
+            Canvas canvas = childNode.startRecording();
+            canvas.drawColor(Color.BLUE);
+        }
+        childNode.endRecording();
+        childNode.setElevation(20f);
+
+        {
+            Canvas canvas = mContent.startRecording();
+            canvas.drawColor(Color.WHITE);
+            canvas.enableZ();
+            canvas.drawRenderNode(childNode);
+            canvas.disableZ();
+        }
+        mContent.endRecording();
+        mRenderer.setContentRoot(mContent);
+        mRenderer.createRenderRequest()
+                .setWaitForPresent(true)
+                .syncAndDraw();
+        Image image = reader.acquireNextImage();
+        Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(),
+                ColorSpace.get(ColorSpace.Named.SRGB));
+        mImageView.setImageBitmap(bitmap);
+        image.close();
+    }
+}
diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
index cb6a83d..39608a4 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
@@ -56,7 +56,7 @@
     @Test
     public void ColorExtractor_extractWhenInitialized() {
         ExtractionType type = mock(Tonal.class);
-        new ColorExtractor(mContext, type);
+        new ColorExtractor(mContext, type, true);
         // 1 for lock and 1 for system
         verify(type, times(2))
                 .extractInto(any(), any(), any(), any());
@@ -83,7 +83,7 @@
                     outGradientColorsDark.set(colorsExpectedDark);
                     outGradientColorsExtraDark.set(colorsExpectedExtraDark);
                 };
-        ColorExtractor extractor = new ColorExtractor(mContext, type);
+        ColorExtractor extractor = new ColorExtractor(mContext, type, true);
 
         GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM,
                 ColorExtractor.TYPE_NORMAL);
@@ -98,7 +98,7 @@
     public void addOnColorsChangedListener_invokesListener() {
         ColorExtractor.OnColorsChangedListener mockedListeners =
                 mock(ColorExtractor.OnColorsChangedListener.class);
-        ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext));
+        ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true);
         extractor.addOnColorsChangedListener(mockedListeners);
 
         extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.RED), null, null),
diff --git a/tests/JankBench/Android.bp b/tests/JankBench/Android.bp
new file mode 100644
index 0000000..166639d
--- /dev/null
+++ b/tests/JankBench/Android.bp
@@ -0,0 +1,21 @@
+android_test {
+    name: "JankBench",
+    manifest: "app/src/main/AndroidManifest.xml",
+    sdk_version: "current",
+    // omit gradle 'build' dir
+    srcs: ["app/src/main/java/**/*.java"],
+    // use appcompat/support lib from the tree, so improvements/
+    // regressions are reflected in test data
+    resource_dirs: ["app/src/main/res"],
+    static_libs: [
+        "com.google.android.material_material",
+        "androidx.legacy_legacy-support-v4",
+        "androidx.appcompat_appcompat",
+        "androidx.cardview_cardview",
+        "androidx.recyclerview_recyclerview",
+        "androidx.leanback_leanback",
+        "apache-commons-math",
+        "junit",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk
deleted file mode 100644
index 89c21b7..0000000
--- a/tests/JankBench/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_USE_AAPT2 := true
-
-# omit gradle 'build' dir
-LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java)
-
-# use appcompat/support lib from the tree, so improvements/
-# regressions are reflected in test data
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/app/src/main/res \
-
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    com.google.android.material_material \
-    androidx.legacy_legacy-support-v4 \
-    androidx.appcompat_appcompat \
-    androidx.cardview_cardview \
-    androidx.recyclerview_recyclerview \
-    androidx.leanback_leanback \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    apache-commons-math \
-    junit
-
-
-LOCAL_PACKAGE_NAME := JankBench
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/JankBench/app/src/main/jni/Android.bp.converted b/tests/JankBench/app/src/main/jni/Android.bp.converted
new file mode 100644
index 0000000..9fecf15
--- /dev/null
+++ b/tests/JankBench/app/src/main/jni/Android.bp.converted
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libnativebench",
+    cflags: [
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+    ],
+    srcs: [
+        "Bench.cpp",
+        "WorkerPool.cpp",
+        "test.cpp",
+    ],
+    host_ldlibs: ["-llog"],
+}
diff --git a/tests/JankBench/app/src/main/jni/Android.mk b/tests/JankBench/app/src/main/jni/Android.mk
deleted file mode 100644
index 8ba874de..0000000
--- a/tests/JankBench/app/src/main/jni/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-LOCAL_SDK_VERSION := 26
-
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS = -Wno-unused-parameter
-
-LOCAL_MODULE:= libnativebench
-
-LOCAL_SRC_FILES := \
-	Bench.cpp \
-	WorkerPool.cpp \
-	test.cpp
-
-LOCAL_LDLIBS := -llog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 40d4eff..0967ad3 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -71,7 +71,7 @@
 
 # RollbackTest
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
 LOCAL_PACKAGE_NAME := RollbackTest
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
@@ -84,10 +84,21 @@
   $(ROLLBACK_TEST_APP_BV1) \
   $(ROLLBACK_TEST_APP_BV2) \
   $(ROLLBACK_TEST_APEX_V2)
+LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml
 LOCAL_SDK_VERSION := system_current
 LOCAL_TEST_CONFIG := RollbackTest.xml
 include $(BUILD_PACKAGE)
 
+# StagedRollbackTest
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, StagedRollbackTest/src)
+LOCAL_MODULE := StagedRollbackTest
+LOCAL_MODULE_TAGS := tests
+LOCAL_JAVA_LIBRARIES := tradefed
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_TEST_CONFIG := StagedRollbackTest.xml
+include $(BUILD_HOST_JAVA_LIBRARY)
+
 # Clean up local variables
 ROLLBACK_TEST_APP_AV1 :=
 ROLLBACK_TEST_APP_AV2 :=
diff --git a/tests/RollbackTest/README.txt b/tests/RollbackTest/README.txt
new file mode 100644
index 0000000..c0b718a
--- /dev/null
+++ b/tests/RollbackTest/README.txt
@@ -0,0 +1,23 @@
+This directory contains a test for the rollback manager service.
+
+Directory structure
+===================
+RollbackTest
+  - device driven test for rollbacks not involving staged rollbacks.
+
+StagedRollbackTest
+  - device driven test for staged rollbacks.
+
+TestApp
+  - source for dummy apks used in testing.
+
+TestApex
+  - source for dummy apex modules used in testing.
+
+Running the tests
+=================
+
+You can manually run the tests as follows:
+
+  atest RollbackTest
+  atest StagedRollbackTest
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index adbad56..ac39f85 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -21,5 +21,9 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+
+        <!-- Exclude the StagedRollbackTest tests, which needs to be specially
+             driven from the StagedRollbackTest host test -->
+        <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" />
     </test>
 </configuration>
diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
similarity index 100%
rename from tests/RollbackTest/AndroidManifest.xml
rename to tests/RollbackTest/RollbackTest/AndroidManifest.xml
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
similarity index 100%
rename from tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
rename to tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
similarity index 100%
rename from tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
rename to tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
similarity index 92%
rename from tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
rename to tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index f07ae9f..f3edf09 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -16,6 +16,10 @@
 
 package com.android.tests.rollback;
 
+import static com.android.tests.rollback.RollbackTestUtils.assertPackageRollbackInfoEquals;
+import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals;
+import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
@@ -24,7 +28,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.VersionedPackage;
-import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.Handler;
@@ -42,7 +45,6 @@
 import org.junit.runners.JUnit4;
 
 import java.util.Collections;
-import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.SynchronousQueue;
@@ -617,17 +619,6 @@
         }
     }
 
-    // Helper function to test the value of a PackageRollbackInfo
-    private void assertPackageRollbackInfoEquals(String packageName,
-            long versionRolledBackFrom, long versionRolledBackTo,
-            PackageRollbackInfo info) {
-        assertEquals(packageName, info.getPackageName());
-        assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName());
-        assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode());
-        assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
-        assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
-    }
-
     /**
      * Test bad update automatic rollback.
      */
@@ -713,23 +704,6 @@
         }
     }
 
-    // Helper function to test the value of a RollbackInfo with single package
-    private void assertRollbackInfoEquals(String packageName,
-            long versionRolledBackFrom, long versionRolledBackTo,
-            RollbackInfo info, VersionedPackage... causePackages) {
-        assertNotNull(info);
-        assertEquals(1, info.getPackages().size());
-        assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
-                info.getPackages().get(0));
-        assertEquals(causePackages.length, info.getCausePackages().size());
-        for (int i = 0; i < causePackages.length; ++i) {
-            assertEquals(causePackages[i].getPackageName(),
-                    info.getCausePackages().get(i).getPackageName());
-            assertEquals(causePackages[i].getLongVersionCode(),
-                    info.getCausePackages().get(i).getLongVersionCode());
-        }
-    }
-
     // Helper function to test that the given rollback info is a rollback for
     // the atomic set {A2, B2} -> {A1, B1}.
     private void assertRollbackInfoForAandB(RollbackInfo rollback) {
@@ -743,23 +717,4 @@
             assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1));
         }
     }
-
-    // Helper function to return the RollbackInfo with a given package in the
-    // list of rollbacks. Throws an assertion failure if there is more than
-    // one such rollback info. Returns null if there are no such rollback
-    // infos.
-    private RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
-            String packageName) {
-        RollbackInfo found = null;
-        for (RollbackInfo rollback : rollbacks) {
-            for (PackageRollbackInfo info : rollback.getPackages()) {
-                if (packageName.equals(info.getPackageName())) {
-                    assertNull(found);
-                    found = rollback;
-                    break;
-                }
-            }
-        }
-        return found;
-    }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
new file mode 100644
index 0000000..280ee1d
--- /dev/null
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -0,0 +1,375 @@
+/*
+ * 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.tests.rollback;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Utilities to facilitate testing rollbacks.
+ */
+class RollbackTestUtils {
+
+    private static final String TAG = "RollbackTest";
+
+    static RollbackManager getRollbackManager() {
+        Context context = InstrumentationRegistry.getContext();
+        RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
+        if (rm == null) {
+            throw new AssertionError("Failed to get RollbackManager");
+        }
+        return rm;
+    }
+
+    /**
+     * Returns the version of the given package installed on device.
+     * Returns -1 if the package is not currently installed.
+     */
+    static long getInstalledVersion(String packageName) {
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager pm = context.getPackageManager();
+        try {
+            PackageInfo info = pm.getPackageInfo(packageName, 0);
+            return info.getLongVersionCode();
+        } catch (PackageManager.NameNotFoundException e) {
+            return -1;
+        }
+    }
+
+    private static void assertStatusSuccess(Intent result) {
+        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == -1) {
+            throw new AssertionError("PENDING USER ACTION");
+        } else if (status > 0) {
+            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+        }
+    }
+
+    /**
+     * Uninstalls the given package.
+     * Does nothing if the package is not installed.
+     * @throws AssertionError if package can't be uninstalled.
+     */
+    static void uninstall(String packageName) throws InterruptedException, IOException {
+        // No need to uninstall if the package isn't installed.
+        if (getInstalledVersion(packageName) == -1) {
+            return;
+        }
+
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager packageManager = context.getPackageManager();
+        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
+        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    /**
+     * Commit the given rollback.
+     * @throws AssertionError if the rollback fails.
+     */
+    static void rollback(int rollbackId, VersionedPackage... causePackages)
+            throws InterruptedException {
+        RollbackManager rm = getRollbackManager();
+        rm.commitRollback(rollbackId, Arrays.asList(causePackages),
+                LocalIntentSender.getIntentSender());
+        Intent result = LocalIntentSender.getIntentSenderResult();
+        int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                RollbackManager.STATUS_FAILURE);
+        if (status != RollbackManager.STATUS_SUCCESS) {
+            String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message);
+        }
+    }
+
+    /**
+     * Installs the apk with the given name.
+     *
+     * @param resourceName name of class loader resource for the apk to
+     *        install.
+     * @param enableRollback if rollback should be enabled.
+     * @throws AssertionError if the installation fails.
+     */
+    static void install(String resourceName, boolean enableRollback)
+            throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        PackageInstaller.Session session = null;
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        if (enableRollback) {
+            params.setEnableRollback();
+        }
+        int sessionId = packageInstaller.createSession(params);
+        session = packageInstaller.openSession(sessionId);
+
+        ClassLoader loader = RollbackTest.class.getClassLoader();
+        try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+             InputStream is = loader.getResourceAsStream(resourceName);) {
+            byte[] buffer = new byte[4096];
+            int n;
+            while ((n = is.read(buffer)) >= 0) {
+                packageInSession.write(buffer, 0, n);
+            }
+        }
+
+        // Commit the session (this will start the installation workflow).
+        session.commit(LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    /** Launches {@code packageName} with {@link Intent#ACTION_MAIN}. */
+    static void launchPackage(String packageName)
+            throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setPackage(packageName);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        context.startActivity(intent);
+    }
+
+    /**
+     * Installs the apks with the given resource names as an atomic set.
+     * <p>
+     * In case of staged installs, this function will return succesfully after
+     * the staged install has been committed and is ready for the device to
+     * reboot.
+     *
+     * @param staged if the rollback should be staged.
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of the class loader resource for the apks to
+     *        install.
+     * @throws AssertionError if the installation fails.
+     */
+    private static void install(boolean staged, boolean enableRollback,
+            String... resourceNames) throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+
+        PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        multiPackageParams.setMultiPackage();
+        if (staged) {
+            multiPackageParams.setStaged();
+        }
+        if (enableRollback) {
+            // TODO: Do we set this on the parent params, the child params, or
+            // both?
+            multiPackageParams.setEnableRollback();
+        }
+        int multiPackageId = packageInstaller.createSession(multiPackageParams);
+        PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
+
+        for (String resourceName : resourceNames) {
+            PackageInstaller.Session session = null;
+            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            if (staged) {
+                params.setStaged();
+            }
+            if (enableRollback) {
+                params.setEnableRollback();
+            }
+            int sessionId = packageInstaller.createSession(params);
+            session = packageInstaller.openSession(sessionId);
+
+            ClassLoader loader = RollbackTest.class.getClassLoader();
+            try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+                 InputStream is = loader.getResourceAsStream(resourceName);) {
+                byte[] buffer = new byte[4096];
+                int n;
+                while ((n = is.read(buffer)) >= 0) {
+                    packageInSession.write(buffer, 0, n);
+                }
+            }
+            multiPackage.addChildSessionId(sessionId);
+        }
+
+        // Commit the session (this will start the installation workflow).
+        multiPackage.commit(LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+
+        if (staged) {
+            waitForSessionReady(multiPackageId);
+        }
+    }
+
+    /**
+     * Installs the apks with the given resource names as an atomic set.
+     *
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of the class loader resource for the apks to
+     *        install.
+     * @throws AssertionError if the installation fails.
+     */
+    static void installMultiPackage(boolean enableRollback, String... resourceNames)
+            throws InterruptedException, IOException {
+        install(false, enableRollback, resourceNames);
+    }
+
+    /**
+     * Installs the apks with the given resource names as a staged atomic set.
+     *
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of the class loader resource for the apks to
+     *        install.
+     * @throws AssertionError if the installation fails.
+     */
+    static void installStaged(boolean enableRollback, String... resourceNames)
+            throws InterruptedException, IOException {
+        install(true, enableRollback, resourceNames);
+    }
+
+    static void adoptShellPermissionIdentity(String... permissions) {
+        InstrumentationRegistry
+            .getInstrumentation()
+            .getUiAutomation()
+            .adoptShellPermissionIdentity(permissions);
+    }
+
+    static void dropShellPermissionIdentity() {
+        InstrumentationRegistry
+            .getInstrumentation()
+            .getUiAutomation()
+            .dropShellPermissionIdentity();
+    }
+
+    /**
+     * Returns the RollbackInfo with a given package in the list of rollbacks.
+     * Throws an assertion failure if there is more than one such rollback
+     * info. Returns null if there are no such rollback infos.
+     */
+    static RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
+            String packageName) {
+        RollbackInfo found = null;
+        for (RollbackInfo rollback : rollbacks) {
+            for (PackageRollbackInfo info : rollback.getPackages()) {
+                if (packageName.equals(info.getPackageName())) {
+                    assertNull(found);
+                    found = rollback;
+                    break;
+                }
+            }
+        }
+        return found;
+    }
+
+    /**
+     * Asserts that the given PackageRollbackInfo has the expected package
+     * name and versions.
+     */
+    static void assertPackageRollbackInfoEquals(String packageName,
+            long versionRolledBackFrom, long versionRolledBackTo,
+            PackageRollbackInfo info) {
+        assertEquals(packageName, info.getPackageName());
+        assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName());
+        assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode());
+        assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
+        assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
+    }
+
+    /**
+     * Asserts that the given RollbackInfo has a single package with expected
+     * package name and versions.
+     */
+    static void assertRollbackInfoEquals(String packageName,
+            long versionRolledBackFrom, long versionRolledBackTo,
+            RollbackInfo info, VersionedPackage... causePackages) {
+        assertNotNull(info);
+        assertEquals(1, info.getPackages().size());
+        assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
+                info.getPackages().get(0));
+        assertEquals(causePackages.length, info.getCausePackages().size());
+        for (int i = 0; i < causePackages.length; ++i) {
+            assertEquals(causePackages[i].getPackageName(),
+                    info.getCausePackages().get(i).getPackageName());
+            assertEquals(causePackages[i].getLongVersionCode(),
+                    info.getCausePackages().get(i).getLongVersionCode());
+        }
+    }
+
+    /**
+     * Waits for the given session to be marked as ready.
+     * Throws an assertion if the session fails.
+     */
+    static void waitForSessionReady(int sessionId) {
+        BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
+        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                PackageInstaller.SessionInfo info =
+                        intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
+                if (info != null && info.getSessionId() == sessionId) {
+                    if (info.isSessionReady() || info.isSessionFailed()) {
+                        try {
+                            sessionStatus.put(info);
+                        } catch (InterruptedException e) {
+                            Log.e(TAG, "Failed to put session info.", e);
+                        }
+                    }
+                }
+            }
+        };
+        IntentFilter sessionUpdatedFilter =
+                new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
+
+        Context context = InstrumentationRegistry.getContext();
+        context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
+
+        PackageInstaller installer = context.getPackageManager().getPackageInstaller();
+        PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
+
+        try {
+            if (info.isSessionReady() || info.isSessionFailed()) {
+                sessionStatus.put(info);
+            }
+
+            info = sessionStatus.take();
+            context.unregisterReceiver(sessionUpdatedReceiver);
+            if (info.isSessionFailed()) {
+                throw new AssertionError(info.getStagedSessionErrorMessage());
+            }
+        } catch (InterruptedException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
new file mode 100644
index 0000000..297bf86
--- /dev/null
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 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.tests.rollback;
+
+import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals;
+import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
+
+import android.Manifest;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for rollback of staged installs.
+ * <p>
+ * Note: These tests require reboot in between test phases. They are run
+ * specially so that the testFooEnableRollback, testFooCommitRollback, and
+ * testFooConfirmRollback phases of each test are run in order with reboots in
+ * between them.
+ */
+@RunWith(JUnit4.class)
+public class StagedRollbackTest {
+
+    private static final String TAG = "RollbackTest";
+    private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
+
+    /**
+     * Adopts common shell permissions needed for rollback tests.
+     */
+    @Before
+    public void adoptShellPermissions() {
+        RollbackTestUtils.adoptShellPermissionIdentity(
+                Manifest.permission.INSTALL_PACKAGES,
+                Manifest.permission.DELETE_PACKAGES,
+                Manifest.permission.MANAGE_ROLLBACKS);
+    }
+
+    /**
+     * Drops shell permissions needed for rollback tests.
+     */
+    @After
+    public void dropShellPermissions() {
+        RollbackTestUtils.dropShellPermissionIdentity();
+    }
+
+
+    /**
+     * Test basic rollbacks. Enable rollback phase.
+     */
+    @Test
+    public void testBasicEnableRollback() throws Exception {
+        RollbackTestUtils.uninstall(TEST_APP_A);
+        assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        RollbackTestUtils.installStaged(true, "RollbackTestAppAv2.apk");
+
+        // At this point, the host test driver will reboot the device and run
+        // testBasicCommitRollback().
+    }
+
+    /**
+     * Test basic rollbacks. Commit rollback phase.
+     */
+    @Test
+    public void testBasicCommitRollback() throws Exception {
+        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                rm.getAvailableRollbacks(), TEST_APP_A);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertTrue(rollback.isStaged());
+
+        RollbackTestUtils.rollback(rollback.getRollbackId());
+
+        rollback = getUniqueRollbackInfoForPackage(
+                rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertTrue(rollback.isStaged());
+        assertNotEquals(-1, rollback.getCommittedSessionId());
+
+        RollbackTestUtils.waitForSessionReady(rollback.getCommittedSessionId());
+
+        // The app should not be rolled back until after reboot.
+        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        // At this point, the host test driver will reboot the device and run
+        // testBasicConfirmRollback().
+    }
+
+    /**
+     * Test basic rollbacks. Confirm rollback phase.
+     */
+    @Test
+    public void testBasicConfirmRollback() throws Exception {
+        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertTrue(rollback.isStaged());
+        assertNotEquals(-1, rollback.getCommittedSessionId());
+    }
+}
diff --git a/tests/RollbackTest/StagedRollbackTest.xml b/tests/RollbackTest/StagedRollbackTest.xml
new file mode 100644
index 0000000..2750d37
--- /dev/null
+++ b/tests/RollbackTest/StagedRollbackTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 the staged rollback tests">
+    <option name="test-suite-tag" value="StagedRollbackTest" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="RollbackTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" />
+    </test>
+</configuration>
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
new file mode 100644
index 0000000..6cb0dd0
--- /dev/null
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 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.tests.rollback.host;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs the staged rollback tests.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class StagedRollbackTest extends BaseHostJUnit4Test {
+
+    /**
+     * Runs the given phase of a test by calling into the device.
+     * Throws an exception if the test phase fails.
+     * <p>
+     * For example, <code>runPhase("testBasicEnableRollback");</code>
+     */
+    private void runPhase(String phase) throws Exception {
+        assertTrue(runDeviceTests("com.android.tests.rollback",
+                    "com.android.tests.rollback.StagedRollbackTest",
+                    phase));
+    }
+
+    /**
+     * Tests staged rollbacks.
+     */
+    @Test
+    public void testBasic() throws Exception {
+        runPhase("testBasicEnableRollback");
+        getDevice().reboot();
+        runPhase("testBasicCommitRollback");
+        getDevice().reboot();
+        runPhase("testBasicConfirmRollback");
+    }
+}
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
index c1d95ac..6be93a0 100644
--- a/tests/RollbackTest/TEST_MAPPING
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "RollbackTest"
+    },
+    {
+      "name": "StagedRollbackTest"
     }
   ]
 }
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
deleted file mode 100644
index 60c7a59..0000000
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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.tests.rollback;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.VersionedPackage;
-import android.content.rollback.RollbackManager;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-/**
- * Utilities to facilitate testing rollbacks.
- */
-class RollbackTestUtils {
-
-    private static final String TAG = "RollbackTest";
-
-    static RollbackManager getRollbackManager() {
-        Context context = InstrumentationRegistry.getContext();
-        RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
-        if (rm == null) {
-            throw new AssertionError("Failed to get RollbackManager");
-        }
-        return rm;
-    }
-
-    /**
-     * Returns the version of the given package installed on device.
-     * Returns -1 if the package is not currently installed.
-     */
-    static long getInstalledVersion(String packageName) {
-        Context context = InstrumentationRegistry.getContext();
-        PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo info = pm.getPackageInfo(packageName, 0);
-            return info.getLongVersionCode();
-        } catch (PackageManager.NameNotFoundException e) {
-            return -1;
-        }
-    }
-
-    private static void assertStatusSuccess(Intent result) {
-        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status > 0) {
-            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
-        }
-    }
-
-    /**
-     * Uninstalls the given package.
-     * Does nothing if the package is not installed.
-     * @throws AssertionError if package can't be uninstalled.
-     */
-    static void uninstall(String packageName) throws InterruptedException, IOException {
-        // No need to uninstall if the package isn't installed.
-        if (getInstalledVersion(packageName) == -1) {
-            return;
-        }
-
-        Context context = InstrumentationRegistry.getContext();
-        PackageManager packageManager = context.getPackageManager();
-        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
-        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
-    }
-
-    /**
-     * Commit the given rollback.
-     * @throws AssertionError if the rollback fails.
-     */
-    static void rollback(int rollbackId, VersionedPackage... causePackages)
-            throws InterruptedException {
-        RollbackManager rm = getRollbackManager();
-        rm.commitRollback(rollbackId, Arrays.asList(causePackages),
-                LocalIntentSender.getIntentSender());
-        Intent result = LocalIntentSender.getIntentSenderResult();
-        int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                RollbackManager.STATUS_FAILURE);
-        if (status != RollbackManager.STATUS_SUCCESS) {
-            String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message);
-        }
-    }
-
-    /**
-     * Installs the apk with the given name.
-     *
-     * @param resourceName name of class loader resource for the apk to
-     *        install.
-     * @param enableRollback if rollback should be enabled.
-     * @throws AssertionError if the installation fails.
-     */
-    static void install(String resourceName, boolean enableRollback)
-            throws InterruptedException, IOException {
-        Context context = InstrumentationRegistry.getContext();
-        PackageInstaller.Session session = null;
-        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
-        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        if (enableRollback) {
-            params.setEnableRollback();
-        }
-        int sessionId = packageInstaller.createSession(params);
-        session = packageInstaller.openSession(sessionId);
-
-        ClassLoader loader = RollbackTest.class.getClassLoader();
-        try (OutputStream packageInSession = session.openWrite("package", 0, -1);
-             InputStream is = loader.getResourceAsStream(resourceName);) {
-            byte[] buffer = new byte[4096];
-            int n;
-            while ((n = is.read(buffer)) >= 0) {
-                packageInSession.write(buffer, 0, n);
-            }
-        }
-
-        // Commit the session (this will start the installation workflow).
-        session.commit(LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
-    }
-
-    /** Launches {@code packageName} with {@link Intent#ACTION_MAIN}. */
-    static void launchPackage(String packageName)
-            throws InterruptedException, IOException {
-        Context context = InstrumentationRegistry.getContext();
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.setPackage(packageName);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        context.startActivity(intent);
-    }
-
-    /**
-     * Installs the apks with the given resource names as an atomic set.
-     *
-     * @param enableRollback if rollback should be enabled.
-     * @param resourceNames names of the class loader resource for the apks to
-     *        install.
-     * @throws AssertionError if the installation fails.
-     */
-    static void installMultiPackage(boolean enableRollback, String... resourceNames)
-            throws InterruptedException, IOException {
-        Context context = InstrumentationRegistry.getContext();
-        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
-
-        PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        multiPackageParams.setMultiPackage();
-        if (enableRollback) {
-            // TODO: Do we set this on the parent params, the child params, or
-            // both?
-            multiPackageParams.setEnableRollback();
-        }
-        int multiPackageId = packageInstaller.createSession(multiPackageParams);
-        PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
-
-        for (String resourceName : resourceNames) {
-            PackageInstaller.Session session = null;
-            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-            if (enableRollback) {
-                params.setEnableRollback();
-            }
-            int sessionId = packageInstaller.createSession(params);
-            session = packageInstaller.openSession(sessionId);
-
-            ClassLoader loader = RollbackTest.class.getClassLoader();
-            try (OutputStream packageInSession = session.openWrite("package", 0, -1);
-                 InputStream is = loader.getResourceAsStream(resourceName);) {
-                byte[] buffer = new byte[4096];
-                int n;
-                while ((n = is.read(buffer)) >= 0) {
-                    packageInSession.write(buffer, 0, n);
-                }
-            }
-            multiPackage.addChildSessionId(sessionId);
-        }
-
-        // Commit the session (this will start the installation workflow).
-        multiPackage.commit(LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
-    }
-
-    static void adoptShellPermissionIdentity(String... permissions) {
-        InstrumentationRegistry
-            .getInstrumentation()
-            .getUiAutomation()
-            .adoptShellPermissionIdentity(permissions);
-    }
-
-    static void dropShellPermissionIdentity() {
-        InstrumentationRegistry
-            .getInstrumentation()
-            .getUiAutomation()
-            .dropShellPermissionIdentity();
-    }
-}
diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp
new file mode 100644
index 0000000..e0608e2
--- /dev/null
+++ b/tests/UiBench/Android.bp
@@ -0,0 +1,19 @@
+android_test {
+    name: "UiBench",
+    sdk_version: "current",
+    min_sdk_version: "21",
+    // omit gradle 'build' dir
+    srcs: ["src/**/*.java"],
+    // use appcompat/support lib from the tree, so improvements/
+    // regressions are reflected in test data
+    resource_dirs: ["res"],
+    static_libs: [
+        "com.google.android.material_material",
+        "androidx.legacy_legacy-support-v4",
+        "androidx.appcompat_appcompat",
+        "androidx.cardview_cardview",
+        "androidx.recyclerview_recyclerview",
+        "androidx.leanback_leanback",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
deleted file mode 100644
index 608bf2f..0000000
--- a/tests/UiBench/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
-
-# omit gradle 'build' dir
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-# use appcompat/support lib from the tree, so improvements/
-# regressions are reflected in test data
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    com.google.android.material_material \
-    androidx.legacy_legacy-support-v4 \
-    androidx.appcompat_appcompat \
-    androidx.cardview_cardview \
-    androidx.recyclerview_recyclerview \
-    androidx.leanback_leanback
-
-LOCAL_PACKAGE_NAME := UiBench
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsTest/Android.bp b/tests/UsageStatsTest/Android.bp
new file mode 100644
index 0000000..0808b05
--- /dev/null
+++ b/tests/UsageStatsTest/Android.bp
@@ -0,0 +1,8 @@
+android_test {
+    name: "UsageStatsTest",
+    // Only compile source java files in this apk.
+    srcs: ["src/**/*.java"],
+    static_libs: ["androidx.legacy_legacy-support-v4"],
+    certificate: "platform",
+    platform_apis: true,
+}
diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk
deleted file mode 100644
index 5eed38c..0000000
--- a/tests/UsageStatsTest/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PACKAGE_NAME := UsageStatsTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp
new file mode 100644
index 0000000..c7c4b10
--- /dev/null
+++ b/tests/libs-permissions/Android.bp
@@ -0,0 +1,29 @@
+java_library {
+    name: "com.android.test.libs.product",
+    installable: true,
+    product_specific: true,
+    srcs: ["product/java/**/*.java"],
+    required: ["com.android.test.libs.product.xml"],
+}
+
+prebuilt_etc {
+    name: "com.android.test.libs.product.xml",
+    src: "product/com.android.test.libs.product.xml",
+    sub_dir: "permissions",
+    product_specific: true,
+}
+
+java_library {
+    name: "com.android.test.libs.product_services",
+    installable: true,
+    product_services_specific: true,
+    srcs: ["product_services/java/**/*.java"],
+    required: ["com.android.test.libs.product_services.xml"],
+}
+
+prebuilt_etc {
+    name: "com.android.test.libs.product_services.xml",
+    src: "product_services/com.android.test.libs.product_services.xml",
+    sub_dir: "permissions",
+    product_services_specific: true,
+}
diff --git a/tests/libs-permissions/Android.mk b/tests/libs-permissions/Android.mk
deleted file mode 100644
index f4250c8..0000000
--- a/tests/libs-permissions/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.test.libs.product
-LOCAL_PRODUCT_MODULE := true
-LOCAL_SRC_FILES := $(call all-java-files-under, product/java)
-LOCAL_REQUIRED_MODULES := com.android.test.libs.product.xml
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.test.libs.product.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions
-LOCAL_SRC_FILES:= product/com.android.test.libs.product.xml
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.test.libs.product_services
-LOCAL_PRODUCT_SERVICES_MODULE := true
-LOCAL_SRC_FILES := $(call all-java-files-under, product_services/java)
-LOCAL_REQUIRED_MODULES := com.android.test.libs.product_services.xml
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.test.libs.product_services.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/permissions
-LOCAL_SRC_FILES:= product_services/com.android.test.libs.product_services.xml
-include $(BUILD_PREBUILT)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a7c95c7..a10fb4e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@
 import android.net.NetworkParcelable;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -245,7 +246,7 @@
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
     @Mock INetd mMockNetd;
-    @Mock NetworkStack mNetworkStack;
+    @Mock NetworkStackClient mNetworkStack;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -901,11 +902,14 @@
 
         public void setUids(Set<UidRange> uids) {
             mNetworkCapabilities.setUids(uids);
-            updateCapabilities();
+            updateCapabilities(null /* defaultNetwork */);
         }
 
         @Override
         public int getNetId() {
+            if (mMockNetworkAgent == null) {
+                return NETID_UNSET;
+            }
             return mMockNetworkAgent.getNetwork().netId;
         }
 
@@ -927,12 +931,13 @@
         }
 
         @Override
-        public void updateCapabilities() {
-            if (!mConnected) return;
-            super.updateCapabilities();
-            // Because super.updateCapabilities will update the capabilities of the agent but not
-            // the mock agent, the mock agent needs to know about them.
+        public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+            if (!mConnected) return null;
+            super.updateCapabilities(defaultNetwork);
+            // Because super.updateCapabilities will update the capabilities of the agent but
+            // not the mock agent, the mock agent needs to know about them.
             copyCapabilitiesToNetworkAgent();
+            return new NetworkCapabilities(mNetworkCapabilities);
         }
 
         private void copyCapabilitiesToNetworkAgent() {
@@ -1077,6 +1082,11 @@
         }
 
         @Override
+        protected NetworkStackClient getNetworkStack() {
+            return mNetworkStack;
+        }
+
+        @Override
         public WakeupMessage makeWakeupMessage(
                 Context context, Handler handler, String cmdName, int cmd, Object obj) {
             return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -3817,11 +3827,14 @@
     }
 
     @Test
-    public void testNattSocketKeepalives() throws Exception {
+    public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception {
         final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
         doTestNattSocketKeepalivesWithExecutor(executorSingleThread);
         executorSingleThread.shutdown();
+    }
 
+    @Test
+    public void testNattSocketKeepalives_InlineExecutor() throws Exception {
         final Executor executorInline = (Runnable r) -> r.run();
         doTestNattSocketKeepalivesWithExecutor(executorInline);
     }
@@ -3963,6 +3976,7 @@
         testSocket2.close();
 
         mWiFiNetworkAgent.disconnect();
+        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
     }
 
     @Test
@@ -4686,6 +4700,7 @@
 
         vpnNetworkAgent.connect(false);
         mMockVpn.connect();
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
 
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4718,6 +4733,7 @@
 
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
+        vpnNetworkAgent.setUids(ranges);
 
         genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4751,12 +4767,11 @@
     }
 
     @Test
-    public void testVpnWithAndWithoutInternet() {
+    public void testVpnWithoutInternet() {
         final int uid = Process.myUid();
 
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
-        defaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
@@ -4778,11 +4793,30 @@
         vpnNetworkAgent.disconnect();
         defaultCallback.assertNoCallback();
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
+    @Test
+    public void testVpnWithInternet() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+
+        defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
         vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
         mMockVpn.connect();
+
         defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -4790,14 +4824,6 @@
         defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
-        ranges.clear();
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
-        mMockVpn.connect();
-        defaultCallback.assertNoCallback();
-
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
@@ -4900,6 +4926,70 @@
     }
 
     @Test
+    public void testNullUnderlyingNetworks() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN)
+                .addTransportType(TRANSPORT_VPN)
+                .build();
+        NetworkCapabilities nc;
+        mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+        vpnNetworkCallback.assertNoCallback();
+
+        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.connect();
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+        nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        // By default, VPN is set to track default network (i.e. its underlying networks is null).
+        // In case of no default network, VPN is considered metered.
+        assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+        // Connect to Cell; Cell is the default network.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Connect to WiFi; WiFi is the new default.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+                && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+        // the capabilities.
+        mCellNetworkAgent.disconnect();
+
+        // Disconnect wifi too. Now we have no default network.
+        mWiFiNetworkAgent.disconnect();
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        mMockVpn.disconnect();
+    }
+
+    @Test
     public void testNetworkBlockedStatus() {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e877a8f..5057443 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,7 +38,6 @@
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
-import android.net.NetworkStack;
 import android.os.INetworkManagementService;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -75,16 +74,12 @@
     @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
-    @Mock NetworkStack mNetworkStack;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
-        when(mCtx.getSystemServiceName(NetworkStack.class))
-                .thenReturn(Context.NETWORK_STACK_SERVICE);
-        when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
 
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a4a735d..533d7ad 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -195,10 +195,6 @@
     }
 
     public class MockIpServerDependencies extends IpServer.Dependencies {
-        MockIpServerDependencies() {
-            super(null);
-        }
-
         @Override
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
                 InterfaceParams ifParams) {
@@ -266,7 +262,7 @@
         }
 
         @Override
-        public IpServer.Dependencies getIpServerDependencies(Context context) {
+        public IpServer.Dependencies getIpServerDependencies() {
             return mIpServerDependencies;
         }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..b5d1ff9 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -28,6 +28,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -89,6 +90,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -566,7 +568,7 @@
 
         final NetworkCapabilities caps = new NetworkCapabilities();
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +579,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile},
                 caps,
@@ -591,7 +593,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +604,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +615,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile, wifi},
                 caps,
@@ -775,6 +777,16 @@
         // V4 does not, but V6 has sufficient coverage again
         lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
         assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+        lp.clear();
+        // V4-unreachable route should not be treated as sufficient coverage
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+        assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+        lp.clear();
+        // V6-unreachable route should not be treated as sufficient coverage
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+        assertFalse(Vpn.providesRoutesToMostDestinations(lp));
     }
 
     @Test
diff --git a/tests/privapp-permissions/Android.bp b/tests/privapp-permissions/Android.bp
new file mode 100644
index 0000000..ca7864f
--- /dev/null
+++ b/tests/privapp-permissions/Android.bp
@@ -0,0 +1,61 @@
+android_app {
+    name: "PrivAppPermissionTest",
+    sdk_version: "current",
+    privileged: true,
+    manifest: "system/AndroidManifest.xml",
+    required: ["privapp-permissions-test.xml"],
+}
+
+prebuilt_etc {
+    name: "privapp-permissions-test.xml",
+    src: "system/privapp-permissions-test.xml",
+    sub_dir: "permissions",
+}
+
+android_app {
+    name: "VendorPrivAppPermissionTest",
+    sdk_version: "current",
+    privileged: true,
+    manifest: "vendor/AndroidManifest.xml",
+    vendor: true,
+    required: ["vendorprivapp-permissions-test.xml"],
+}
+
+prebuilt_etc {
+    name: "vendorprivapp-permissions-test.xml",
+    src: "vendor/privapp-permissions-test.xml",
+    sub_dir: "permissions",
+    proprietary: true,
+}
+
+android_app {
+    name: "ProductPrivAppPermissionTest",
+    sdk_version: "current",
+    privileged: true,
+    manifest: "product/AndroidManifest.xml",
+    product_specific: true,
+    required: ["productprivapp-permissions-test.xml"],
+}
+
+prebuilt_etc {
+    name: "productprivapp-permissions-test.xml",
+    src: "product/privapp-permissions-test.xml",
+    sub_dir: "permissions",
+    product_specific: true,
+}
+
+android_app {
+    name: "ProductServicesPrivAppPermissionTest",
+    sdk_version: "current",
+    privileged: true,
+    manifest: "product_services/AndroidManifest.xml",
+    product_services_specific: true,
+    required: ["product_servicesprivapp-permissions-test.xml"],
+}
+
+prebuilt_etc {
+    name: "product_servicesprivapp-permissions-test.xml",
+    src: "product_services/privapp-permissions-test.xml",
+    sub_dir: "permissions",
+    product_services_specific: true,
+}
diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk
deleted file mode 100644
index 1149b8a..0000000
--- a/tests/privapp-permissions/Android.mk
+++ /dev/null
@@ -1,64 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := PrivAppPermissionTest
-LOCAL_SDK_VERSION := current
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
-LOCAL_REQUIRED_MODULES := privapp-permissions-test.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp-permissions-test.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES:= system/privapp-permissions-test.xml
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := VendorPrivAppPermissionTest
-LOCAL_SDK_VERSION := current
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
-LOCAL_VENDOR_MODULE := true
-LOCAL_REQUIRED_MODULES := vendorprivapp-permissions-test.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := vendorprivapp-permissions-test.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/permissions
-LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest
-LOCAL_SDK_VERSION := current
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := product/AndroidManifest.xml
-LOCAL_PRODUCT_MODULE := true
-LOCAL_REQUIRED_MODULES := productprivapp-permissions-test.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := productprivapp-permissions-test.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions
-LOCAL_SRC_FILES:= product/privapp-permissions-test.xml
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := ProductServicesPrivAppPermissionTest
-LOCAL_SDK_VERSION := current
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := product_services/AndroidManifest.xml
-LOCAL_PRODUCT_SERVICES_MODULE := true
-LOCAL_REQUIRED_MODULES := product_servicesprivapp-permissions-test.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := product_servicesprivapp-permissions-test.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/permissions
-LOCAL_SRC_FILES:= product_services/privapp-permissions-test.xml
-include $(BUILD_PREBUILT)
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5f664f5..9832485 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -435,7 +435,7 @@
   const size_t NS = pool->size();
   for (size_t s=0; s<NS; s++) {
     String8 str = pool->string8ObjectAt(s);
-    printer->Print(StringPrintf("String #%zd: %s\n", s, str.string()));
+    printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
   }
 }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 2f8ca2d..d0237f8 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1084,7 +1084,7 @@
   // Create a overlayable entry grouping that represents this <overlayable>
   auto overlayable = std::make_shared<Overlayable>(
       overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
-      out_resource->source);
+      source_);
 
   bool error = false;
   std::string comment;
@@ -1113,6 +1113,13 @@
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
+      if (current_policies == OverlayableItem::Policy::kNone) {
+        diag_->Error(DiagMessage(element_source)
+                         << "<item> within an <overlayable> must be inside a <policy> block");
+        error = true;
+        continue;
+      }
+
       // Items specify the name and type of resource that should be overlayable
       Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!item_name) {
@@ -1169,6 +1176,8 @@
             current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
             current_policies |= OverlayableItem::Policy::kVendor;
+          } else if (trimmed_part == "signature") {
+            current_policies |= OverlayableItem::Policy::kSignature;
           } else {
             diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
@@ -1176,6 +1185,11 @@
             continue;
           }
         }
+      } else {
+        diag_->Error(DiagMessage(element_source)
+                         << "<policy> must have a 'type' attribute");
+        error = true;
+        continue;
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
       diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
@@ -1623,7 +1637,9 @@
       if (!(plural->values[index] = ParseXml(
                 parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
         error = true;
+        continue;
       }
+
       plural->values[index]->SetSource(item_source);
 
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 827c7de..25b76b0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -894,8 +894,10 @@
 TEST_F(ResourceParserTest, ParseOverlayable) {
   std::string input = R"(
       <overlayable name="Name" actor="overlay://theme">
-        <item type="string" name="foo" />
-        <item type="drawable" name="bar" />
+          <policy type="signature">
+            <item type="string" name="foo" />
+            <item type="drawable" name="bar" />
+          </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
@@ -906,7 +908,7 @@
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
@@ -915,7 +917,7 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -931,7 +933,6 @@
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
   std::string input = R"(
       <overlayable name="Name">
-        <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
         </policy>
@@ -944,23 +945,18 @@
         <policy type="public">
           <item type="string" name="faz" />
         </policy>
+        <policy type="signature">
+          <item type="string" name="foz" />
+        </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
-
-  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  result_overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
@@ -986,6 +982,30 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
+  std::string input = R"(
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
+        <policy>
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 7ca99ea..32dfd26 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -92,6 +92,9 @@
 
     // The resource can be overlaid by any overlay on the product partition.
     kProduct = 0x08,
+
+    // The resource can be overlaid by any overlay signed with the same signature as its actor.
+    kSignature = 0x010,
   };
 
   std::shared_ptr<Overlayable> overlayable;
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ab4805f..0032960 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -727,7 +727,7 @@
           // This must be a FileReference.
           std::unique_ptr<FileReference> file_ref =
               util::make_unique<FileReference>(dst_pool->MakeRef(
-                  str, StringPool::Context(StringPool::Context::kHighPriority, config), data));
+                  str, StringPool::Context(StringPool::Context::kHighPriority, config)));
           if (type == ResourceType::kRaw) {
             file_ref->type = ResourceFile::Type::kUnknown;
           } else if (util::EndsWith(*file_ref->path, ".xml")) {
@@ -739,7 +739,7 @@
         }
 
         // There are no styles associated with this string, so treat it as a simple string.
-        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config), data));
+        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
       }
     } break;
 
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 73b568e..a2fd7c6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -138,10 +138,10 @@
 
 // Represents a set of overlayable resources.
 message Overlayable {
-  // The name of the <overlyabale>.
+  // The name of the <overlayable>.
   string name = 1;
 
-  // The location of the <overlyabale> declaration in the source.
+  // The location of the <overlayable> declaration in the source.
   Source source = 2;
 
   // The component responsible for enabling and disabling overlays targeting this <overlayable>.
@@ -151,10 +151,12 @@
 // Represents an overlayable <item> declaration within an <overlayable> tag.
 message OverlayableItem {
   enum Policy {
-    PUBLIC = 0;
-    SYSTEM = 1;
-    VENDOR = 2;
-    PRODUCT = 3;
+    NONE = 0;
+    PUBLIC = 1;
+    SYSTEM = 2;
+    VENDOR = 3;
+    PRODUCT = 4;
+    SIGNATURE = 5;
   }
 
   // The location of the <item> declaration in source.
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index a8c2666..8eabd32 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -165,13 +165,12 @@
   return MakeRefImpl(str, Context{}, true);
 }
 
-StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context,
-                                    Maybe<size_t> index) {
-  return MakeRefImpl(str, context, true, index);
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
+  return MakeRefImpl(str, context, true);
 }
 
 StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
-                                        bool unique, Maybe<size_t> index) {
+                                        bool unique) {
   if (unique) {
     auto range = indexed_strings_.equal_range(str);
     for (auto iter = range.first; iter != range.second; ++iter) {
@@ -181,26 +180,15 @@
     }
   }
 
-  const size_t size = strings_.size();
-  // Insert the string at the end of the string vector if no index is specified
-  const size_t insertion_index = index ? index.value() : size;
-
   std::unique_ptr<Entry> entry(new Entry());
   entry->value = str.to_string();
   entry->context = context;
-  entry->index_ = insertion_index;
+  entry->index_ = strings_.size();
   entry->ref_ = 0;
   entry->pool_ = this;
 
   Entry* borrow = entry.get();
-  if (insertion_index == size) {
-    strings_.emplace_back(std::move(entry));
-  } else {
-    // Allocate enough space for the string at the index
-    strings_.resize(std::max(insertion_index + 1, size));
-    strings_[insertion_index] = std::move(entry);
-  }
-
+  strings_.emplace_back(std::move(entry));
   indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
   return Ref(borrow);
 }
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 115d5d3..1006ca9 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -166,8 +166,7 @@
 
   // Adds a string to the pool, unless it already exists, with a context object that can be used
   // when sorting the string pool. Returns a reference to the string in the pool.
-  Ref MakeRef(const android::StringPiece& str, const Context& context,
-              Maybe<size_t> index = {});
+  Ref MakeRef(const android::StringPiece& str, const Context& context);
 
   // Adds a string from another string pool. Returns a reference to the string in the string pool.
   Ref MakeRef(const Ref& ref);
@@ -211,8 +210,7 @@
 
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
 
-  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique,
-                  Maybe<size_t> index = {});
+  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
   void ReAssignIndices();
 
   std::vector<std::unique_ptr<Entry>> strings_;
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 648be7d..9a7238b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -84,24 +84,6 @@
   EXPECT_THAT(ref_c.index(), Eq(2u));
 }
 
-TEST(StringPoolTest, AssignStringIndex) {
-  StringPool pool;
-
-  StringPool::Ref ref_a = pool.MakeRef("0", StringPool::Context{}, 0u);
-  StringPool::Ref ref_b = pool.MakeRef("1", StringPool::Context{}, 1u);
-  StringPool::Ref ref_c = pool.MakeRef("5", StringPool::Context{}, 5u);
-  StringPool::Ref ref_d = pool.MakeRef("2", StringPool::Context{}, 2u);
-  StringPool::Ref ref_e = pool.MakeRef("4", StringPool::Context{}, 4u);
-  StringPool::Ref ref_f = pool.MakeRef("3", StringPool::Context{}, 3u);
-
-  EXPECT_THAT(ref_a.index(), Eq(0u));
-  EXPECT_THAT(ref_b.index(), Eq(1u));
-  EXPECT_THAT(ref_d.index(), Eq(2u));
-  EXPECT_THAT(ref_f.index(), Eq(3u));
-  EXPECT_THAT(ref_e.index(), Eq(4u));
-  EXPECT_THAT(ref_c.index(), Eq(5u));
-}
-
 TEST(StringPoolTest, PruneStringsWithNoReferences) {
   StringPool pool;
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 7a74ba9..0cf86cc 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -43,7 +43,8 @@
 
 class IApkSerializer {
  public:
-  IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+  IApkSerializer(IAaptContext* context, const Source& source) : context_(context),
+                                                                source_(source) {}
 
   virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                             IArchiveWriter* writer, uint32_t compression_flags) = 0;
@@ -167,7 +168,7 @@
       std::unique_ptr<io::IData> data = file->file->OpenAsData();
       if (!data) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                         << "failed to open file " << *file->path);
+                                          << "failed to open file " << *file->path);
         return false;
       }
 
@@ -175,7 +176,7 @@
       std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
       if (xml == nullptr) {
         context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
-                                          << error);
+                                                               << error);
         return false;
       }
 
@@ -256,9 +257,6 @@
 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
             ApkFormat output_format, TableFlattenerOptions table_flattener_options,
             XmlFlattenerOptions xml_flattener_options) {
-  // Do not change the ordering of strings in the values string pool
-  table_flattener_options.sort_stringpool_entries = false;
-
   unique_ptr<IApkSerializer> serializer;
   if (output_format == ApkFormat::kBinary) {
     serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
@@ -274,7 +272,7 @@
   io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
   if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
                                 output_writer, (manifest != nullptr && manifest->WasCompressed())
-                                ? ArchiveEntry::kCompress : 0u)) {
+                                               ? ArchiveEntry::kCompress : 0u)) {
     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
                                      << "failed to serialize AndroidManifest.xml");
     return 1;
@@ -303,8 +301,7 @@
               if (files_written.insert(*file->path).second) {
                 if (!serializer->SerializeFile(file, output_writer)) {
                   context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                       << "failed to serialize file "
-                                                       << *file->path);
+                                                   << "failed to serialize file " << *file->path);
                   return 1;
                 }
               }
@@ -338,7 +335,7 @@
 
     if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
       context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                       << "failed to copy file " << path);
+                                           << "failed to copy file " << path);
       return 1;
     }
   }
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 8463046..22edd2f 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -268,6 +268,7 @@
   bool update_proguard_spec = false;
   OutputFormat output_format = OutputFormat::kApk;
   std::unordered_set<std::string> extensions_to_not_compress;
+  Maybe<std::regex> regex_to_not_compress;
 };
 
 // A sampling of public framework resource IDs.
@@ -377,11 +378,18 @@
   }
 }
 
+// TODO(rtmitchell): turn this function into a variable that points to a method that retrieves the
+// compression flag
 uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
   if (options_.do_not_compress_anything) {
     return 0;
   }
 
+  if (options_.regex_to_not_compress
+      && std::regex_search(str.to_string(), options_.regex_to_not_compress.value())) {
+    return 0;
+  }
+
   for (const std::string& extension : options_.extensions_to_not_compress) {
     if (util::EndsWith(str, extension)) {
       return 0;
@@ -400,7 +408,8 @@
 
 static bool IsVectorElement(const std::string& name) {
   return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
-         name == "objectAnimator" || name == "gradient" || name == "animated-selector";
+         name == "objectAnimator" || name == "gradient" || name == "animated-selector" ||
+         name == "set";
 }
 
 template <typename T>
@@ -1530,7 +1539,11 @@
     for (auto& entry : merged_assets) {
       uint32_t compression_flags = ArchiveEntry::kCompress;
       std::string extension = file::GetExtension(entry.first).to_string();
-      if (options_.extensions_to_not_compress.count(extension) > 0) {
+
+      if (options_.do_not_compress_anything
+          || options_.extensions_to_not_compress.count(extension) > 0
+          || (options_.regex_to_not_compress
+              && std::regex_search(extension, options_.regex_to_not_compress.value()))) {
         compression_flags = 0u;
       }
 
@@ -1558,6 +1571,7 @@
     file_flattener_options.keep_raw_values = keep_raw_values;
     file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
     file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress;
+    file_flattener_options.regex_to_not_compress = options_.regex_to_not_compress;
     file_flattener_options.no_auto_version = options_.no_auto_version;
     file_flattener_options.no_version_vectors = options_.no_version_vectors;
     file_flattener_options.no_version_transitions = options_.no_version_transitions;
@@ -2165,6 +2179,20 @@
     }
   }
 
+  if (no_compress_regex) {
+    std::string regex = no_compress_regex.value();
+    if (util::StartsWith(regex, "@")) {
+      const std::string path = regex.substr(1, regex.size() -1);
+      std::string error;
+      if (!file::AppendSetArgsFromFile(path, &options_.extensions_to_not_compress, &error)) {
+        context.GetDiagnostics()->Error(DiagMessage(path) << error);
+        return 1;
+      }
+    } else {
+      options_.regex_to_not_compress = GetRegularExpression(no_compress_regex.value());
+    }
+  }
+
   // Populate some default no-compress extensions that are already compressed.
   options_.extensions_to_not_compress.insert(
       {".jpg",   ".jpeg", ".png",  ".gif", ".wav",  ".mp2",  ".mp3",  ".ogg",
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 590a6bb..1fc149a 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -17,6 +17,8 @@
 #ifndef AAPT2_LINK_H
 #define AAPT2_LINK_H
 
+#include <regex>
+
 #include "Command.h"
 #include "Diagnostics.h"
 #include "Resource.h"
@@ -63,6 +65,7 @@
   bool no_xml_namespaces = false;
   bool do_not_compress_anything = false;
   std::unordered_set<std::string> extensions_to_not_compress;
+  Maybe<std::regex> regex_to_not_compress;
 
   // Static lib options.
   bool no_static_lib_packages = false;
@@ -250,6 +253,11 @@
         &options_.do_not_compress_anything);
     AddOptionalSwitch("--keep-raw-values", "Preserve raw attribute values in xml files.",
         &options_.keep_raw_values);
+    AddOptionalFlag("--no-compress-regex",
+        "Do not compress extensions matching the regular expression. Remember to\n"
+            " use the '$' symbol for end of line. Uses a non case-sensitive\n"
+            " ECMAScript regular expression grammar.",
+        &no_compress_regex);
     AddOptionalSwitch("--warn-manifest-validation",
         "Treat manifest validation errors as warnings.",
         &options_.manifest_fixer_options.warn_validation);
@@ -283,6 +291,7 @@
   std::vector<std::string> configs_;
   Maybe<std::string> preferred_density_;
   Maybe<std::string> product_list_;
+  Maybe<std::string> no_compress_regex;
   bool legacy_x_flag_ = false;
   bool require_localization_ = false;
   bool verbose_ = false;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 792120e..e2c65ba7 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -435,4 +435,11 @@
   }
 }
 
+std::regex GetRegularExpression(const std::string &input) {
+  // Standard ECMAScript grammar plus case insensitive.
+  std::regex case_insensitive(
+      input, std::regex_constants::icase | std::regex_constants::ECMAScript);
+  return case_insensitive;
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index cf1443e..2a7c62e 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -17,6 +17,8 @@
 #ifndef AAPT_SPLIT_UTIL_H
 #define AAPT_SPLIT_UTIL_H
 
+#include <regex>
+
 #include "androidfw/StringPiece.h"
 
 #include "AppInfo.h"
@@ -72,6 +74,9 @@
 // versionCodeMajor if the version code requires more than 32 bits.
 void SetLongVersionCode(xml::Element* manifest, uint64_t version_code);
 
+// Returns a case insensitive regular expression based on the input.
+std::regex GetRegularExpression(const std::string &input);
+
 }  // namespace aapt
 
 #endif /* AAPT_SPLIT_UTIL_H */
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index f92f1e3..7e49261 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -383,4 +383,12 @@
   EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
 }
 
+TEST(UtilTest, RegularExperssions) {
+  std::string valid(".bc$");
+  std::regex expression = GetRegularExpression(valid);
+  EXPECT_TRUE(std::regex_search("file.abc", expression));
+  EXPECT_TRUE(std::regex_search("file.123bc", expression));
+  EXPECT_FALSE(std::regex_search("abc.zip", expression));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 40aaa05..59eb9ec 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -401,7 +401,6 @@
     if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
       Visibility visibility;
       visibility.level = Visibility::Level::kPublic;
-      visibility.source = source_.WithLine(0);
       if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
         return false;
       }
@@ -448,7 +447,6 @@
                                                       arraysize(header->name)));
   overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
                                                        arraysize(header->name)));
-  overlayable->source = source_.WithLine(0);
 
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
@@ -473,6 +471,10 @@
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
         policies |= OverlayableItem::Policy::kProduct;
       }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
+        policies |= OverlayableItem::Policy::kSignature;
+      }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
           ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
@@ -491,7 +493,6 @@
         }
 
         OverlayableItem overlayable_item(overlayable);
-        overlayable_item.source = source_.WithLine(0);
         overlayable_item.policies = policies;
         if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 9d341cc..d677317 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -274,7 +274,9 @@
       FlattenLibrarySpec(buffer);
     }
 
-    FlattenOverlayable(buffer);
+    if (!FlattenOverlayable(buffer)) {
+      return false;
+    }
 
     pkg_writer.Finish();
     return true;
@@ -468,23 +470,29 @@
           overlayable_chunk = &chunk;
         }
 
+        if (item.policies == 0) {
+          context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                << "overlayable "
+                                                << entry->name
+                                                << " does not specify policy");
+          return false;
+        }
+
         uint32_t policy_flags = 0;
-        if (item.policies == OverlayableItem::Policy::kNone) {
-          // Encode overlayable entries defined without a policy as publicly overlayable
+        if (item.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        } else {
-          if (item.policies & OverlayableItem::Policy::kPublic) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-          }
-          if (item.policies & OverlayableItem::Policy::kSystem) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kVendor) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kProduct) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-          }
+        }
+        if (item.policies & OverlayableItem::Policy::kSystem) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kVendor) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kProduct) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kSignature) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
         }
 
         auto policy = overlayable_chunk->policy_ids.find(policy_flags);
@@ -702,17 +710,15 @@
 }  // namespace
 
 bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
-  if (options_.sort_stringpool_entries) {
-    // We must do this before writing the resources, since the string pool IDs may change.
-    table->string_pool.Prune();
-    table->string_pool.Sort([](const StringPool::Context &a, const StringPool::Context &b) -> int {
-      int diff = util::compare(a.priority, b.priority);
-      if (diff == 0) {
-        diff = a.config.compare(b.config);
-      }
-      return diff;
-    });
-  }
+  // We must do this before writing the resources, since the string pool IDs may change.
+  table->string_pool.Prune();
+  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    int diff = util::compare(a.priority, b.priority);
+    if (diff == 0) {
+      diff = a.config.compare(b.config);
+    }
+    return diff;
+  });
 
   // Write the ResTable header.
   ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 71330e3..73c1729 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -44,9 +44,6 @@
   // Set of whitelisted resource names to avoid altering in key stringpool
   std::set<std::string> whitelisted_resources;
 
-  // When true, sort the entries in the values string pool by priority and configuration.
-  bool sort_stringpool_entries = true;
-
   // Map from original resource paths to shortened resource paths.
   std::map<std::string, std::string> shortened_path_map;
 };
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index ddc1173..4c5dbec 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -671,9 +671,6 @@
   overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
   overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_item_three(overlayable);
-
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -683,8 +680,6 @@
           .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
           .SetOverlayable(name_two, overlayable_item_two)
-          .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -713,16 +708,6 @@
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
                                        | OverlayableItem::Policy::kProduct
                                        | OverlayableItem::Policy::kVendor);
-
-  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
-  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
-  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
@@ -745,6 +730,8 @@
 
   std::string name_three = "com.app.test:integer/overlayable_three";
   OverlayableItem overlayable_item_three(group_one);
+  overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -793,7 +780,22 @@
   result_overlayable = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
-  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+}
+
+TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aff1b39..06f1bf7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -390,6 +390,9 @@
       case pb::OverlayableItem::PRODUCT:
         out_overlayable->policies |= OverlayableItem::Policy::kProduct;
         break;
+      case pb::OverlayableItem::SIGNATURE:
+        out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index b549e23..eb2b1a2 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -309,6 +309,9 @@
   if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
   }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
+  }
 
   SerializeSourceToPb(overlayable_item.source, source_pool,
                       pb_overlayable_item->mutable_source());
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index cce3939..d369ac4 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -526,6 +526,10 @@
       "FontPack", "overlay://theme"));
   overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
+  OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
+      "IconPack", "overlay://theme"));
+  overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
+
   OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
       "Other", "overlay://customization"));
   overlayable_item_biz.comment ="comment";
@@ -536,6 +540,7 @@
           .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
           .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
           .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
           .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
@@ -576,6 +581,14 @@
   EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
   EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/integration-tests/Android.mk b/tools/aapt2/integration-tests/Android.mk
deleted file mode 100644
index 6361f9b..0000000
--- a/tools/aapt2/integration-tests/Android.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
new file mode 100644
index 0000000..79fb573
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "AaptAutoVersionTest",
+    sdk_version: "current",
+}
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp
new file mode 100644
index 0000000..a94a01f
--- /dev/null
+++ b/tools/aapt2/integration-tests/BasicTest/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "AaptBasicTest",
+    sdk_version: "current",
+}
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.mk b/tools/aapt2/integration-tests/BasicTest/Android.mk
deleted file mode 100644
index d160554..0000000
--- a/tools/aapt2/integration-tests/BasicTest/Android.mk
+++ /dev/null
@@ -1,24 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptBasicTest
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/StaticLibTest/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/Android.mk
deleted file mode 100644
index 6361f9b..0000000
--- a/tools/aapt2/integration-tests/StaticLibTest/Android.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
new file mode 100644
index 0000000..9aadff3
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+
+    name: "AaptTestStaticLib_App",
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    asset_dirs: [
+        "assets",
+        "assets2",
+    ],
+    static_libs: [
+        "AaptTestStaticLib_LibOne",
+        "AaptTestStaticLib_LibTwo",
+    ],
+    aaptflags: [
+        "--no-version-vectors",
+        "--no-version-transitions",
+    ],
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
deleted file mode 100644
index 3cce35d..0000000
--- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptTestStaticLib_App
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    AaptTestStaticLib_LibOne \
-    AaptTestStaticLib_LibTwo
-LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
-include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
new file mode 100644
index 0000000..4c81813
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_library {
+    name: "AaptTestStaticLib_LibOne",
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
deleted file mode 100644
index da25f64..0000000
--- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLib_LibOne
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
new file mode 100644
index 0000000..7c4f7ed
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_library {
+    name: "AaptTestStaticLib_LibTwo",
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    libs: ["AaptTestStaticLib_LibOne"],
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
deleted file mode 100644
index 27a3134..0000000
--- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLib_LibTwo
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
new file mode 100644
index 0000000..68e6148
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "AaptSymlinkTest",
+    sdk_version: "current",
+}
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.mk b/tools/aapt2/integration-tests/SymlinkTest/Android.mk
deleted file mode 100644
index 8da1141..0000000
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.mk
+++ /dev/null
@@ -1,24 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptSymlinkTest
-LOCAL_SDK_VERSION := current
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 7b268bb..604b257 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -251,6 +251,25 @@
   return true;
 }
 
+bool AppendSetArgsFromFile(const StringPiece& path, std::unordered_set<std::string>* out_argset,
+                           std::string* out_error) {
+  std::string contents;
+  if(!ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
+    if (out_error) {
+      *out_error = "failed to read argument-list file";
+    }
+    return false;
+  }
+
+  for (StringPiece line : util::Tokenize(contents, ' ')) {
+    line = util::TrimWhitespace(line);
+    if (!line.empty()) {
+      out_argset->insert(line.to_string());
+    }
+  }
+  return true;
+}
+
 bool FileFilter::SetPattern(const StringPiece& pattern) {
   pattern_tokens_ = util::SplitAndLowercase(pattern, ':');
   return true;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 5839552..481a4cd 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 #include "android-base/macros.h"
@@ -86,6 +87,10 @@
 bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
                         std::string* out_error);
 
+// Reads the file at path and appends each line to the outargset set.
+bool AppendSetArgsFromFile(const android::StringPiece& path,
+                        std::unordered_set<std::string>* out_argset, std::string* out_error);
+
 // Filter that determines which resource files/directories are
 // processed by AAPT. Takes a pattern string supplied by the user.
 // Pattern format is specified in the FileFilter::SetPattern() method.
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 488de87..af9fdfb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -276,14 +276,19 @@
 
     /**
      * Returns the service set identifier (SSID) of the current 802.11 network.
+     * <p>
      * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
-     * quotation marks. Otherwise, it is returned as a string of hex digits. The
-     * SSID may be &lt;unknown ssid&gt; if there is no network currently connected,
-     * or if the caller has insufficient permissions to access the SSID.
-     *
+     * quotation marks. Otherwise, it is returned as a string of hex digits.
+     * The SSID may be
+     * <lt>&lt;unknown ssid&gt;, if there is no network currently connected or if the caller has
+     * insufficient permissions to access the SSID.<lt>
+     * </p>
+     * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
      * always returned the SSID with no quotes around it.
-     * @return the SSID
+     * </p>
+     *
+     * @return the SSID.
      */
     public String getSSID() {
         if (mWifiSsid != null) {
@@ -312,7 +317,13 @@
 
     /**
      * Return the basic service set identifier (BSSID) of the current access point.
-     * The BSSID may be {@code null} if there is no network currently connected.
+     * <p>
+     * The BSSID may be
+     * <lt>{@code null}, if there is no network currently connected.</lt>
+     * <lt>{@code "02:00:00:00:00:00"}, if the caller has insufficient permissions to access the
+     * BSSID.<lt>
+     * </p>
+     *
      * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
      */
     public String getBSSID() {
@@ -511,9 +522,13 @@
 
     /**
      * Each configured network has a unique small integer ID, used to identify
-     * the network when performing operations on the supplicant. This method
-     * returns the ID for the currently connected network.
-     * @return the network ID, or -1 if there is no currently connected network
+     * the network. This method returns the ID for the currently connected network.
+     * <p>
+     * The networkId may be {@code -1} if there is no currently connected network or if the caller
+     * has insufficient permissions to access the network ID.
+     * </p>
+     *
+     * @return the network ID.
      */
     public int getNetworkId() {
         return mNetworkId;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c88eee8..7caace6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1149,9 +1149,6 @@
      * Return a list of all the networks configured for the current foreground
      * user.
      *
-     * Requires the same permissions as {@link #getScanResults}.
-     * If such access is not allowed, this API will always return an empty list.
-     *
      * Not all fields of WifiConfiguration are returned. Only the following
      * fields are filled in:
      * <ul>
@@ -1176,8 +1173,12 @@
      * when auto-connecting to wifi.
      * <b>Compatibility Note:</b> For applications targeting
      * {@link android.os.Build.VERSION_CODES#Q} or above, this API will return an empty list,
-     * except to callers with Carrier privilege which will receive a restricted list only
-     * containing configurations which they created.
+     * except for:
+     * <ul>
+     * <li>Device Owner (DO) & Profile Owner (PO) apps will have access to the full list.
+     * <li>Callers with Carrier privilege will receive a restricted list only containing
+     * configurations which they created.
+     * </ul>
      */
     @Deprecated
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
@@ -2135,17 +2136,26 @@
     }
 
     /**
+     * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)}
+     * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and
+     * {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}.
+     *
      * @return true if this adapter supports Device-to-device RTT
      * @hide
      */
+    @Deprecated
     @SystemApi
     public boolean isDeviceToDeviceRttSupported() {
         return isFeatureSupported(WIFI_FEATURE_D2D_RTT);
     }
 
     /**
+     * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)}
+     * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT}.
+     *
      * @return true if this adapter supports Device-to-AP RTT
      */
+    @Deprecated
     public boolean isDeviceToApRttSupported() {
         return isFeatureSupported(WIFI_FEATURE_D2AP_RTT);
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 72e57a1..8a1b21c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -67,7 +67,7 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
-    /** The frequency used by this group */
+    /** The frequency (in MHz) used by this group */
     private int mFrequency;
 
     /** P2P group started string pattern */
@@ -273,7 +273,7 @@
         this.mNetId = netId;
     }
 
-    /** Get the operating frequency of the p2p group */
+    /** Get the operating frequency (in MHz) of the p2p group */
     public int getFrequency() {
         return mFrequency;
     }